Flask总结
1.配置文件
2.before_request/after_request
3.路由系统
4.视图
5.模板
6.session
7.flash
8.蓝图(blueprint)
flask-组件
1.flask-session
2.DBUtils
3.wtforms
4.SQLAlchemy
5.flask-script
6.flask-migrate
偏函数
上下文管理
用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" 指别名
视图
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"
模板
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个链接吗? 不是
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>
pipreqs:查看安装包依赖
pip3 install pipreqs 进入到项目里:pipreqs ./ --encoding=utf-8 安装依赖 install -r requirement.txt
virtualenv 创建虚拟环境(纯净的环境)
pip3 install virtualenv 在公司中询问是否安装虚拟环境 1.需要指定目录:virtualenv 文件名 virtualenv venv 2.激活 进入到新建项目中的scripts中执行activate cd venv\Scripts 执行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之后