1.配置文件
2.before_request/after_request
3.路由系统
4.视图
5.模板
6.session
7.flash
8.蓝图(blueprint)
flask-组件
1.flask-session
2.DBUtils
3.wtforms
偏函数
上下文管理
用flask写一个hello world文件
from flask import Flask
app=Flask(__name__)
@app.route("/index")
def index():
return "Hello Word"
if __name__=="__main__":
app.run()
配置文件
1.新建settings.py 存放配置文件
class Config(object):
DEBUG = False
SECRET_KEY = "asdfasdfasdf"
#上线环境
class ProductionConfig(Config):
DEBUG = False
#开发环境
class DevelopmentConfig(Config):
DEBUG = True
#测试环境
class TestingConfig(Config):
TESTING = True
其他文件要引入配置文件中的内容 如:
app.config.from_object('settings.DevelopmentConfig')
引入整个py文件:app.config.from_pyfile("settings.py")
before_request和after_request
before_request和after_request
相当于Django中的中间件
after_request的视图中必须有参数,且必须有返回值
1.before_request---->目标请求 ----->after_request
@app.before_request
def a1():
print("a2")
@app.after_request
def c1(response):
print("c1")
return response
def index():
print("index")
return render_template("index.html")
执行顺序:a2--->index---->c1
2.在走before_request时如果返回了值,后面的before_request就不再执行,目标函数也不执行
会把after_request都走一遍
@app.before_request
def a1():
print("a1")
@app.before_request
def d1(response):
print("d1")
return response
@app.after_request
def c1(response):
print("c1")
return response
return response
def index():
print("index")
return render_template("index.html")
执行顺序: a1--->d1--->c1
路由系统
路由的两种注册方法:
1.使用装饰器
@app.route("/login")
2. app.add_url_rule(rule='/login', view_func=index)
1.定义methods
@app.route("/login",methods=['GET','POST'])
2.动态路由
URL: http://www.xx.com/index/1/88
@app.route('/index/<string:nid>/<int:num>')
也可以 @app.route('/index/<nid>/<num>')
def index(a,b):pass
3.endpoint ,为url起别名,根据别名可以反向生成URL(默认endpoint是函数名)
url_for:反向解析
url_for()它把函数名称作为第一个参数。它可以接受任意个关键字参数,每个关键字参数对应URL中的变量
1.自定义endpoint
@app.route('/user/<username>',endpoint="aaa")
def profile(username):print(url_for('aaa',username='zhao') /user/zhao
2.使用默认的endpoint
@app.route('/login')
def login():pass
@app.route('/user/<username>')
def profile(username):pass
with app.test():
print(url_for('login')) /login
print(url_for('login', next='/')) /login?next=/
print(url_for('profile', username='zhao')) /user/zhao
4.支持自定义正则
@app.route('/index/<string:nid>/<int:num>/<regex("d+"):xxxx>')
5.支持FBV和CBV
from flask import Flask,url_for,views
class IndexView(views.MethodView):
methods = ['GET','POST']
decorators = [auth, ]
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='ci')) name="ci" 指别名
View Code
视图
from flask import request,render_template,redirect
1.请求:
request.method
request.args
request.form
request.cookies
request.headers
request.path
request.full_path
request.url
request.files
obj = request.files['the_file_name']
2.响应:
1.return "xxxx" 返回字符串
2.return render_template("index.html",msg="xxx") 返回模板
return render_template('html模板路径',**{"mag="xxx”,arg":12})
3.return redirect() 重定向
4.response = make_response(render_template('index.html'))
5. response.set_cookie('key', 'value')
6. response.delete_cookie('key')
7.return send_file()
def get_img(file_name):
file_path = os.path.join(setting.RESOURCE_IMG_PATH,file_name)
return send_file(file_path)
@app.route('/setco')
def setco():
from flask import make_response
obj = make_response("返回内容...")
obj.set_cookie('a1','123')
return obj
给视图添加装饰器:
- 装饰器必须设置functools.wappers
- 紧挨着放在视图之上
from functools import wraps
def is_login(func):
@wraps(func) #保留函数元信息
def inner(*args,**kwargs):
user_info = session.get('user')
if not user_info:
return redirect('/login')
ret=func()
return ret
return inner
@app.route("/book")
@is_login #必须紧贴着函数
def book():
return "Book"
View Code
模板
Flask使用的是Jinja2模板,所以其语法和Django无差别
1.支持python语法
2.支持模板继承
3.自定义函数
服务端:
def jerd():
return '<h1>jerd</h1>'
@app.route('/login', methods=['GET', 'POST'])
def login():
return render_template('login.html', ww=jerd)
HTML:需要加()和safe
<body>
{{ww()|safe}}
</body>
4.全局函数
1.global:
@app.template_global()
def test4(arg):
return arg + 100
所有视图中都可以调用:
{{ test4(666) }}
2.filter:
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
所有视图中都可以调用:
{{ 1|db(2,3)}}
Session
session的本质是字典
flask的session存放在cookie中
1.导入session
from flask import session
2.设置key值:app.secret_key="xxxxx"
3.操作:
1.设置:session["zhao"]="zgf"
2.获取:session.get("zhao")
3.删除:del session.["zhao"]
在客户端的cookie中能看到session和sessionid
cookie和session的区别?
cookie,是保存在用户浏览器端的键值对,可以用来做用户认证。
session,将用户会话信息保存在服务端{adfasdfasdf:"...",99dfsdfsdfsd:'xxx'},依赖cookie将每个用户的随机字符串保存到用户浏览器上;
django,session默认保存在数据库;django_session表
flask,session默认将加密的数据写用户cookie中。
flash,闪现
基于session实现
from flask import flash,get_flashed_messages
@app.route('/set_flash')
def set_flash():
flash('666',category='error')
flash('999',category='log')
return "设置成功"
@app.route('/get_flash')
def get_flash():
data = get_flashed_messages(category_filter=['error'])
print(data)
return "或成功"
蓝图(blueprint)
app---蓝图----视图
1. 目录结构的划分(解耦)。
2. 单蓝图中应用before_request
3. URL划分
app.register_blueprint(account,url_prefix='/user')
4. 反向生成URL
url_for('蓝图.函数名')
url_for('蓝图.endpoint')
url_for('蓝图.endpoint',nid=1)
print(url_for('account.login'))
在app下注册蓝图
app=Flask(__name__)
app.register_blueprint(bpmanager)
在视图中:
from flask import Blueprint
bpmanager=Blueprint('bpmanager',__name__)
@bpmanager.route("/delete/",methods=["GET","POST"])
def delete():pass
flask-session
1.flask-session的作用:
将flask中的session由加密存到cookie中的方式更换为放置到其他的数据源
如:redis/memcached/filesystem/mongodb/sqlalchemy(数据库)
2.简述flask-session的原理?
1.请求进来走完before-request后走到open-session方法,该方法从cookie中读取session_id
对应的随机字符串。
2.如果未获取到这个字符串,就创建一个随机字符串并在内存中创建个特殊的字典
如果能获取到这个字符串,就根据这个随机字符串去redis中获取原来设置的值,并在在内存创建字典
3.在视图函数中,对内存中的字典进行操作
4.当请求结束时,执行save_session方法,
1.该方法去读取内存中特殊的字典,并将字典序列化成字符串
2.将字符串写到redis中
3.将随机字符串写到cookie中
操作:
1.安装:pip3 install flask-session
2.在配置文件中:
SESSION_TYPE='redis'
SESSION_REDIS = Redis(host='127.0.0.1',port=6379)
3.引入session
from flask_session import Session
app = Flask(__name__)
Session(app)
DBUtils 数据库连接池
1.pip3 install DBUtils
2.配置连接池:
import pymysql
from DBUtils.PooledDB import PooledDB, SharedDBConnection
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=20, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=0, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='day118',
charset='utf8'
)
使用:
conn = POOL.connection()
cursor = conn.cursor()
cursor.execute('select * from users')
result = cursor.fetchall()
conn.close()
将mysql数据库的操作模块化:
def on_open(cur=pymysql.cursors.DictCursor):
conn = POOL.connection()
cursor = conn.cursor(cursor=cur)
return conn,cursor
def on_close(conn,cursor):
cursor.close()
conn.close()
def fetchone(sql,args,cur=pymysql.cursors.DictCursor):
"""
获取单条数据
获得的数据默认以字典的形式,如果要以元祖展示,传参时设置cur=None
"""
conn,cursor = on_open(cur)
cursor.execute(sql, args)
result = cursor.fetchone()
return result
def fetchall(sql,args,cur=pymysql.cursors.DictCursor):
"""
获取多条数据
"""
conn, cursor = on_open(cur)
cursor.execute(sql, args)
result = cursor.fetchall()
return result
def exec_sql(sql,args,cur=pymysql.cursors.DictCursor):
"""
添加/删除/修改
:param sql: insert into table(%s,%s) values(....)
"""
conn, cursor = on_open(cur)
cursor.execute(sql, args)
conn.commit()
1.DBUtils的作用:
创建数据库连接池
2.简述数据库连接池的原理?
1.启动时会在内存中维护一个连接池
2.当请求需要连接数据库时则去连接池中获取一个连接,如果有空闲的连接就去获取
没有则等待或报错
3.使用完毕后,需要将连接归还到连接池中
3.连接池在最开始启动时,最大连接数是100,是创建了100个链接吗?
不是
View Code
wtforms:对用户请求数据做表单验证
pip3 install wtforms
https://www.cnblogs.com/wupeiqi/articles/8202357.html
自定义校验类:
from wtforms.fields import simple
from wtforms.fields import html5
from wtforms.fields import core
from wtforms import widgets
from wtforms import validators
from wtforms import Form
class TestForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired()
], #错误信息
widget=widgets.TextInput(),
render_kw={'class': 'form-control'} #错误信息样式
)
"""
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
pwd_confirm = simple.PasswordField(
label='重复密码',
validators=[
validators.DataRequired(message='重复密码不能为空.'),
validators.EqualTo('pwd', message="两次密码输入不一致")
], #自定义了比较
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
gender = core.RadioField(
label='性别',
choices=(
(1, '男'),
(2, '女'),
),
coerce=int
)
city = core.SelectField(
label='城市',
choices=(
('bj', '北京'),
('sh', '上海'),
)
)
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
favor = core.SelectMultipleField(
label='喜好',
choices=(
(1, '篮球'),
(2, '足球'),
),
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput(),
coerce=int
)
"""
cls_id = core.SelectField(
label='请选择班级',
choices=(
('bj', '北京'),
('sh', '上海'),
)
choices=[],
coerce=int
)
#重写构造方法
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.cls_id.choices = sqlhelper.fetchall(sql='select id,title from classes',args=[],cur=None)
#钩子函数
def validate_name(self,field):
"""
对name进行验证时的钩子函数
:param field:
:return:
"""
if field != 'root':
raise validators.ValidationError("用户名必须是root")
1.choices字段需要设置coerce=int
2.从数据库显示到页面上的数据要重写构造方法进行实时更新
3.钩子函数:validate_字段名():pass 字段验证后执行定义的钩子函数
服务端:
1.get请求:
if request.method == 'GET':
form = TestForm()
return render_template('add_cls.html',form=form)
2.post请求。实例化时添加formdata再验证
form = TestForm(formdata=request.form)
if form.validate():
数据都在form.data
页面:
<form method="post" novalidate>
<p>
{{form.title.label}} : {{form.title}} <span style="color: red">{{form.title.errors.0}}</span>
</p>
<input type="submit" value="添加">
</form>
View Code
pipreqs:查看安装包依赖
pip3 install pipreqs
进入到项目里:pipreqs ./ --encoding=utf-8
安装依赖
install -r requirement.txt
virtualenv 创建虚拟环境(纯净的环境)
pip3 install virtualenv
在公司中询问是否安装虚拟环境
1.需要指定目录:virtualenv 文件名 virtualenv venv
2.激活 进入到新建项目中的scripts中执行activate
cd venvScripts 执行active
命令提示符变了,有个(venv)前缀,表示当前环境是一个名为venv的Python环境。
3.退出:deactivate
此时就回到了正常的环境,现在pip或python均是在系统Python环境下执行。
在linux中安装虚拟环境
1. pip3 install virtualenv
2.为virtualenv创建软连接
ln -s /software/py3/bin/virtualenv /usr/bin/virtualenv1
删除软连接:rm -rf /usr/bin/virtualenv1
3.在当前目录下创建虚拟环境 virtualenv1 venv1
4.激活 source venv1/bin/activate
5.退出:deactivate
偏函数
使用偏函数可以通过有效地“冻结”那些预先确定的参数,然后在运行时,当获得需要的剩余参数后,可以将他们解冻,传递到最终的参数中
应用场景:调用sum函数时,我们已经知道了其中的一个参数,我们可以通过这个参数,重新绑定一个函数,然后加上其他参数去调用即可
def add(a, b):
return a + b;
print(add(3, 5)) #8
使用偏函数
import functools
def add(a,b):
return a+b
func1=functools.partial(add,3)
print(func1) #functools.partial(<function add at 0x0000028C9C2CFBF8>, 2)
print(func1(5)) #8
上下文管理
RequestContext:封装请求上下文的对象
AppContext:封装应用上下文的对象
LocalStack : 通过此类实例来操作Local对象,提供了pop、top、push方法
Local:为每一个线程开辟一块独立的空间,用来存放数据
LocalProxy:通过此类中的方法来取出request属性和session属性
1.
请求上下文:实例化一个RequestContext对象,并将请求的所有相关信息request和session封装到RequestContext对象中
应用上下文:实例化一个AppContext对象,将current_app, g封装到AppContext中
2.通过LocalStack类将RequestContext对象,AppContext对象添加到Local中(调用push方法)
Local类为每一个线程开辟一块独立的空间,创建了一个字典来保存数据,这个字典的key是用线程的唯一标识存放自己的数据
3.在视图函数中调用request时会执行LocalProxy中对应的魔法方法__getattr__或__setitem__
又通过偏函数调用Localstark中top方法去Local获取到数据,取的是列表的最后一个值。
4.请求终止时还是通过LocalStack的pop方法 将Local中将值在列表中pop掉
内置的session流程:
push中会调用session里面open_session方法,通过这个方法帮助我们获取用户原有的session信息,有就获取,没有就返回一个空的类似字典的数据结构,
赋值给对象中的session,当使用的时候触发LocalProxy对像里对魔法方法通过偏函数用Localstark中方法去Local获取到数据
使用完后,调用session对像的save_session方法,将数据加密写到用户的cookie中,这个操作在after_request之后