# 蓝图、请求扩展和中间件
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。
# 简单演示
from flask import Flask,flash,get_flashed_messages app = Flask(__name__) app.secret_key = "DFSFerw" @app.route('/get') def get(): data = get_flashed_messages() #从那个地方拿值,取完一个值后,所有的都要清空 print(data) # 第一次['666', '777', '666'] 第二次 [] return 'Hello,world' @app.route('/set') def set(): flash('666') # 向某个地方设置了一个值,其实时结合session中设置了一个值 return '设置成功' @app.route('/set2',endpoint='n2') def set(): flash('777') # 访问一次给那个地方设置一个值 return '设置成功' if __name__ == '__main__': app.run()
- 应用场景:比如视图函数重定向到另一个函数,记录前一个视图函数的一些参数
flash的附加功能:单独的使用一次的功能并不能满足我们的要求,也就是说,如果我们可以对添加进的数据进行分类,就更好了
@app.route('/get') def get(): data = get_flashed_messages(category_filter=['xxx']) # 获取分类的数据 print(data) # 第一次['777'] 第二次 [] return 'Hello,world' @app.route('/set') def set(): flash('666') return '设置成功' @app.route('/set2',endpoint='n2') def set(): flash('777',category='xxx') #设置分类 return '设置成功'
注意: 由于flash是基于session做的,所以消息的特性也是符合session的,也就是说,如果你设置了两个值进去,突然服务端挂了,重新链接时,仍旧能取出数据
# 请求扩展
**简单实例:**主要是针对before_request、after_requset函数进行学习
from flask import Flask,request app = Flask(__name__) @app.before_request def process_request(*args,**kwargs): # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走, print('请求之前') return None #返回None才能往后走 @app.after_request def process_response(response): print('走了') return response @app.route('/index2',endpoint='n2') def a(): print('index') return 'index视图函数' if __name__ == '__main__': app.run() #访问index2结果: 请求之前 index 走了
# 多个before_request、after_requset执行时
@app.before_request def process_request1(*args,**kwargs): # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走, print(' process_request1 请求之前') return None #返回None才能往后走 @app.before_request def process_request2(*args,**kwargs): # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走, print(' process_request2 请求之前') return None #返回None才能往后走 @app.after_request def process_response1(response): print('process_response1( 走了') return response @app.after_request def process_response2(response): print('process_response2 走了') return response @app.route('/index2',endpoint='n2') def a(): print('index') return 'index视图函数' #访问index2结果: process_request1 请求之前 process_request2 请求之前 index process_response2 走了 process_response1 走了
**定制报错信息:**当发生报错信息的时候,比如常见的404、500等,我们就可以使用自己得定制的界面
@app.errorhandler(404) #这里是定制的404错误,完全可以定制其他的错误码 def error_404(args): return '404错误' #访问不存在的网址时会报此错误
**before_first_request:**只有第一次请求过来的时候才执行这函数
@app.before_first_request def process_request1(*args,**kwargs): print(' 第一次') #第二次刷新就不走了 return None
# 中间件
Flask的中间件主要是针对整个项目的入口,我们可以通过类的重新封装来扩展功能。
# 梳理Flask项目启动流程:
项目启动执行app.run() 函数,点击run函数,在其中执行了
run_simple(host, port, self, **options) # 这里出自动触发self(),也就是触发了app的__call__方法
查看
__call__
方法def __call__(self, environ, start_response): """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" return self.wsgi_app(environ, start_response) #environ是包含了所有请求的数据,start_response是一个发送HTTP响应的函数。
根据以上我们可以扩展中间件
from flask import Flask, flash, redirect, render_template, request app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/') def index1(): return render_template('index.html') @app.route('/set') def index2(): v = request.args.get('p') flash(v) return 'ok' class MiddleWare: def __init__(self,wsgi_app): self.wsgi_app = wsgi_app def __call__(self, *args, **kwargs): print('中间件执行之前') ret = self.wsgi_app(*args, **kwargs) print('中间件执行之后') return ret if __name__ == "__main__": app.wsgi_app = MiddleWare(app.wsgi_app) #自定义自己的wsgi_app方法 app.run(port=9999)
# 蓝图
当我们的逻辑代码开始增多后改怎么办?一个py文件显然不能承载太多,所以我们可以分写在view函数中
相应的代码:
#account.py文件 from . import app @app.route('/login') def login(): return 'login' @app.route('/logout') def logout(): return 'logout' ------------------------- #order.py文件 from . import app @app.route('/order') def order(): return 'order' -------------------------- from . import app @app.route('/user') def user(): return 'user' --------------------------- #init.py文件 from flask import Flask,request app = Flask(__name__) from . import order #必须导入每个文件,否则不会执行相应的py文件相当于没有加载app的附加路由 from . import user from . import account --------------------------- # 主文件,a2.py from views import app if __name__ == '__main__': app.run()
# 使用蓝图帮我们生成目录结构
原理和上面的方式类似,但是根据全面
常见报错信息
报错:AttributeError: 'function' object has no attribute 'name' 原因是: from flask import Blueprint order = Blueprint('order',__name__) # 相当于app一样,知识没有run方法,只能注册给app @orders.route('/order',) def order(): return 'order' #原因是Blueprint的第一个参数和接受变量名相互重复了,修改变量名即可 #orders = Blueprint('order',__name__)
# 蓝图的目录结构
# 蓝图文件部分
#account.py文件 from flask import Blueprint account = Blueprint('account',__name__) # 相当于app一样,知识没有run方法,只能注册给app @account.route('/login',methods=['GET',]) def login(): return 'account 中的 login' @account.route('/logout') def logout(): return 'logout' ------------------------------------ #order.py文件 from flask import Blueprint orders = Blueprint('order',__name__,url_prefix = '/order') # 相当于app一样,知识没有run方法,只能注册给app,和app的区别可以添加前缀 @orders.route('/login',methods=['GET',]) #/order/login可以访问,因为前缀,否则根据注册顺序先account的login执行 def login(): return 'order 中的 login' @orders.route('/order',) def order(): return 'order' ------------------------------------- #user.py文件 from flask import Blueprint users = Blueprint('users', __name__) # 相当于app一样,知识没有run方法,只能注册给app @users.route('/user',endpoint='users') def user(): return 'user'
在包中的蓝图使用
__init__.py
进行整合from flask import Flask,request app = Flask(__name__,template_folder='templates' ,static_folder='statics',static_url_path='/static') #实例一个能run的app from views.account import account #导入所有的模块否则还是无法识别 from views.user import users from views.order import orders app.register_blueprint(account) #将三个蓝图注册到app中,app.run也就相当于所有蓝图run app.register_blueprint(users) app.register_blueprint(orders)
最后在a2.py文件中run
from views import app if __name__ == '__main__': app.run() #整个项目启动
# 同级目录下的蓝图引用
# 目录结构:
# 代码实现
#a4.py文件 from flask import Blueprint bp = Blueprint('app01',__name__) @bp.route('/users') def user(): return 'I am app01' ----------------------------------- #a5.py文件 from flask import Flask from a4 import bp app = Flask(__name__) app.register_blueprint(bp) @app.route('/login') def login(): return 'login' @bp.route('/users') # 默认不执行此视图函数,因为蓝图中已有,可以给蓝图添加url_prefix方法限制,将注册哪一行写下面会报错,因为蓝图始终要注册,相当于要覆盖user def user(): return 'I app' if __name__ == '__main__': app.run()
# 实例化蓝图的参数说明
每个蓝图都可以有自己的模板路径、静态文件、以及url_prefix等等
def __init__( self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, #蓝图的自己的模板,注意:默认是在同级目录下的模板 url_prefix=None, # 自己的url前缀,用于区分不同蓝图的同名函数 subdomain=None, url_defaults=None, root_path=None, cli_group=_sentinel, ):pass #blog = Blueprint('blog',__name__, template_folder='bb'),会默认去同级目录下找bb模板文件夹
# 蓝图的请求扩展
每个蓝图都可以有自己的before_request。请求到来现在app的before_request进行判断,如果是蓝图的路由,就将请求分发下去
#a4.py文件 from flask import Blueprint bp = Blueprint('app01',__name__) @bp.before_request def before_req(*args, **kwargs): print('bp 位置') @bp.route('/users') def user(): return 'I am app01' ----------------------------------- #a5.py文件 from flask import Flask from a4 import bp app = Flask(__name__) app.register_blueprint(bp) @app.before_request def before_req(*args, **kwargs): print('app 位置') @app.route('/login') def login(): return 'login' @bp.route('/users') def user(): return 'I app' if __name__ == '__main__': app.run() #访问/users路径结果: app 位置 bp 位置
# 总结蓝图
蓝图用来给Flask构造目录
1、可以批量分类url
2、分发模板路径/静态文件路径等
3、请求扩展(适用于白名单)
# 蓝图构建大型项目
目录结构:
要点讲解:
创建了两层init函数,最外层用于整合蓝图到app中,内层init用于创建蓝图,以便views中使用 # 外层init from flask import Flask from .admin import admin from .web import web app = Flask(__name__) app.debug = True app.register_blueprint(admin, url_prefix='/admin') app.register_blueprint(web) # 内层init from flask import Blueprint admin = Blueprint( 'admin', __name__, template_folder='templates', static_folder='static' ) from . import views