11. 蓝图¶
随着程序功能越来越多,为了便于组织,我们把程序功能进行模块化划分, 一个模块负责一类具体内务,这种结构称为蓝图。
简单来说,Blueprint 是一个存储操作方法的容器, Flask 可以通过Blueprint来组织URL以及处理请求。
Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:
- 一个应用可以具有多个Blueprint
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名, 即重复使用
- 在一个应用中,一个模块可以注册多次
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,也意味着蓝图可以和主程序基本没有耦合,可以随便复用
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中,类似组件。
使用蓝图大概步骤:
- 定义蓝图,在一个蓝图内完成一类任务
- 使用蓝图,即注册蓝图,然后属于蓝图的访问会自动导入蓝图内进行处理
11.1. 蓝图使用案例¶
我们定义一个蓝图,在我们的教学业务中,学生的注册登录等管理属于用户模块 ,而相应的课程等属于课程模块,我们把这两个定义成蓝图使用。
定义蓝图
通过定义蓝图,定义的时候使用url_prefix
来定义前缀url,这样相同 的前缀就会使用定义好的蓝图,蓝图此时相当于一个子路由。在下面代码中,我们定义了连个蓝图,
bp1
和bp2
, 每个分别对应不同的url
前缀。# 导入Bluepoint from flask import Blueprint # 定义蓝图,注意参数,不懂的通过 ctr+点击, 进入源代码查看注释 bp1 = Blueprint('user', __name__, url_prefix='/user') bp2 = Blueprint('teaching', __name__, url_prefix='/teaching') # 添加业务处理 # 正常操作 @bp1.route("/login") def user_login(): return "User Login" # 正常操作 @bp1.route("/reg") def user_reg(): return "User Reg" # 正常操作 @bp2.route("/get_course") def teaching_get_course(): return "Teaching get_course" # 正常操作 @bp2.route("/add_course") def teaching_add_course(): return "Teaching add_course"
使用蓝图
蓝图的使用相对简单,直接导入后注册,如果url
前缀符合定义的话就会自动的调用蓝图处理。from flask import Flask # 导入庄子啊蓝图的文件 import bp_apps app = Flask(__name__) app.register_blueprint(bp_apps.bp1) app.register_blueprint(bp_apps.bp2) if __name__ == "__main__": app.run()
在经过上述定义后,我们访问相应url后会有蓝图进行相应处理,比如
访问http://127.0.0.1:5000/teaching/add_course
后会显示对应蓝图的对应处理结果。
11.2. 蓝图运行机制¶
- 蓝图保存了一组将来可以在应用对象上执行的操作
- 当在应用对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表
- 蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
- 当执行应用对象的 register_blueprint() 方法时,应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用应用对象的 add_url_rule() 方法,这将真正的修改应用对象的路由表
11.3. 蓝图的url前缀¶
当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)
在应用最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个前缀, 这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可
url_for 蓝图中更可以使用
url_for
, 我们在蓝图定义的时候会定义蓝图的name
关键字值,在 地下路由中也会使用endpoint
定义端点名称,如果需要使用url_for
,则可以直接用点号操作。# name='user' bp1 = Blueprint('user', __name__, url_prefix='/user') ... ... # endpoint='test' @bp1.route("/test", endpoint='test') def user_test(): # 返回的时候使用点号操作 return url_for("user.test")