Flask初探五( Blueprint / url_for / endpoint )
Blueprint (蓝图)
Blueprints are the recommended way to implement larger or more
pluggable applications in Flask 0.7 and later.
Blueprint 是为了更方便实现模块化开发而诞生的.
模块化
为什么要模块化?
在一个py 文件中写成百上千或者更多个接口, 相信大部分或者绝大部分人都不愿意维护这样的代码, 其次所有接口都写在一个文件中不利于团队协作. 基于以上两点,分模块 / 团队协作 的模块化就显的很有必要了.
Blueprint 与模块
一般来说,都会以功能进行模块划分, 这里就不讨论如何划分了. 假设有首页 / 新闻页 / 登录 三个模块.
原始写法
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/news')
def index():
return 'index'
@app.route('/login')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
接口通过app.route 将规则 和 视图函数进行绑定, 存在同名函数,绑定失败
导入Blueprint
# 导入Blueprint
from flask import Blueprint
from flask import Flask
# 实例化Blueprint 对象
# 蓝图名, import_name ,规则前缀
index_blue = Blueprint("index", __name__, url_prefix="/index")
news_blue = Blueprint("news", __name__, url_prefix="/news")
login_blue = Blueprint("login", __name__, url_prefix="/login")
app = Flask(__name__)
# 用Blueprint 对象实现接口
@index_blue.route('/')
def index():
return 'index'
@news_blue.route('/')
def index():
return 'news'
@login_blue.route('/')
def index():
return 'index'
# 注册Blueprint
app.register_blueprint(index_blue)
app.register_blueprint(news_blue)
app.register_blueprint(login_blue)
if __name__ == '__main__':
print(app.url_map)
app.run(debug=True)
运行结果
Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
<Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
<Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
<Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
Blueprint 的使用步骤
- 实例化Blueprint 对象, 需要导入Blueprint 类
- 用Blueprint 对象实现实现接口
- 注册Blueprint
虽然用Blueprint 实现了接口,但是接口依然在一个文件中, 接下来进行拆分
模块化
main.py
from flask import Flask
from modules import index_blue, news_blue, login_blue
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(index_blue)
app.register_blueprint(news_blue)
app.register_blueprint(login_blue)
if __name__ == '__main__':
print(app.url_map)
app.run(debug=True)
__init__.py
# 导入Blueprint
from flask import Blueprint
# 实例化蓝图对象
index_blue = Blueprint("index", __name__, url_prefix="/index")
news_blue = Blueprint("news", __name__, url_prefix="/news")
login_blue = Blueprint("login", __name__, url_prefix="/login")
from . import index
from . import news
from . import login
index.py
from . import index_blue
@index_blue.route('/')
def index():
return 'index'
login.py
from . import login_blue
@login_blue.route('/')
def index():
return 'login'
news.py
from . import news_blue
@news_blue.route('/')
def index():
return 'news'
运行结果
Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
<Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
<Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
<Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
蓝图的作用原理
猜测: 未使用蓝图时, url_map 存放的是形如 <Rule ‘/index/’ (HEAD, GET, OPTIONS) -> index> 这样的Rule 对象使用蓝图之后,url_map 存放的是形如 <Rule ‘/index/’ (HEAD, GET, OPTIONS) -> index.index> 这样的Rule 对象.
所以蓝图是通过改变视图函数的端点的方式区分视图函数的.
endpoint
在flask初探二 中简单的探究了endpoint, 这里将探究蓝图与endpoint 之间的关系
蓝图的route
def route(self, rule, **options):
"""Like :meth:`Flask.route` but for a blueprint. The endpoint for the
:func:`url_for` function is prefixed with the name of the blueprint.
"""
def decorator(f):
endpoint = options.pop("endpoint", f.__name__)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
和 flask 的route 基本一样
蓝图的add_url_rule
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
the :func:`url_for` function is prefixed with the name of the blueprint.
"""
if endpoint:
assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
self.record(lambda s:
s.add_url_rule(rule, endpoint, view_func, **options))
BlueprintSetupState 的add_url_rule
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
"""A helper method to register a rule (and optionally a view function)
to the application. The endpoint is automatically prefixed with the
blueprint's name.
"""
if self.url_prefix:
rule = self.url_prefix + rule
options.setdefault('subdomain', self.subdomain)
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
defaults = self.url_defaults
if 'defaults' in options:
defaults = dict(defaults, **options.pop('defaults'))
# 重点在这
self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
view_func, defaults=defaults, **options)
循着源码的运行,可以看到蓝图最终是通过调用 app.add_url_rule 的方式将规则和视图函数进行的绑定, 区别是在调用的时候对endpoint 进行的传值, 从而印证了我们开始的猜测.
结论
- 蓝图是通过改变endpoint 的方式区分视图函数的.
- endpoint 默认情况下是视图函数名,
- 使用蓝图后, endpoint 为 蓝图名.视图函数名
endpoint 与 url_for
url_for
def url_for(endpoint, **values):
...(省略)...
从url_for 方法的定义可以看出, url_for需要的是endpoint
- 在不使用蓝图的情况下, 可以直接使用函数名字符串得到URL
@app.route("/")
def index():
pass
url_for("index")
- 使用蓝图时, 使用 蓝图名.视图函数名 字符串得到URL
login_blue = Blueprint("login", __name__, url_prefix="/login")
@login_blue.route('/')
def index():
return 'login'
url_for("login.index") # 得到 /login/
到此结 DragonFangQy 2018.6.30