你可以把蓝图理解成另一个简化版的Flask类,它实现了许多和Flask类相似的方法,并定义了一些蓝本专用的方法。

每一个蓝图都是一个休眠的操作子集,只有注册到程序上才会获得生命。

蓝图的大多数方法并不直接执行逻辑代码,而是把执行操作的函数作为参数传递给Blueprint.record()方法或Blueprint.record_once()方法。

首先 , 我们得说说蓝图的作用
蓝图 (Blueprint) 实现了应用的模块化 , 使用蓝图让应用层次清晰 , 开发者可以更容易的开发和维护项目 , 蓝图通常作用于相同的 URL 前缀 , 比如/user/:id , /user/profile 这样的地址都以 /user 开头 , 那么他们就可以放在一个模块中

构建蓝图

我们从示例开始 :

myapplication/simple_page.py

  1. from flask import Blueprint, render_template, abort
  2. from jinja2 import TemplateNotFound
  3. # Blueprint类与Flask类一样,都继承了_PackageBoundObject
  4. # Blueprint相当于Flask的子应用
  5. simple_page = Blueprint('simple_page', __name__,
  6. template_folder='templates')
  7. @simple_page.route('/', defaults={'page': 'index'})
  8. @simple_page.route('/<page>')
  9. def show(page):
  10. try:
  11. return render_template('pages/%s.html' % page)
  12. except TemplateNotFound:
  13. abort(404)
  14. Copy

Blueprint 类与 Flask 类非常相似 , 它就像是一个应用 , 上面代码中 , 首先实例化一个应用对象 , 虽然绑定路由与视图函数 , 然而它与 Flask 的不同之处就在于 , route 完成的操作并没有真的完成路由的添加 , 而是完成了一个函数的添加 , 我们看看源码 :

  1. def route(self, rule, **options):
  2. def decorator(f):
  3. # 与Flask类中的route方法不同的是,蓝图中将视图函数的函数名作为endpoint
  4. endpoint = options.pop("endpoint", f.__name__)
  5. self.add_url_rule(rule, endpoint, f, **options)
  6. return f
  7. return decorator
  8. Copy

Blueprint.add_url_rule() 如下 :

  1. def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
  2. if endpoint:
  3. assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
  4. if view_func and hasattr(view_func, '__name__'):
  5. assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots"
  6. # 传入一个匿名函数,该函数将在蓝图注册时被调用
  7. self.record(lambda s:
  8. s.add_url_rule(rule, endpoint, view_func, **options))
  9. Copy

Blueprint.record() 如下 :

  1. def record(self, func):
  2. if self._got_registered_once and self.warn_on_modifications:
  3. from warnings import warn
  4. warn(Warning('The blueprint was already registered once '
  5. 'but is getting modified now. These changes '
  6. 'will not show up.'))
  7. # 将函数追加到deferred_functions中
  8. self.deferred_functions.append(func)
  9. Copy

至此 , 我们已经完成了一个蓝图的构建 , 但是此时路由并没有注册 , 它仅仅将注册路由的函数放入 deferred_functions 中 , 等待蓝图注册时被调用

注册蓝图

现在我们需要把构建好的蓝图注册到我们的应用中 , 如下 :

  1. from flask import Flask
  2. from myapplication.simple_page import simple_page
  3. app = Flask(__name__)
  4. app.register_blueprint(simple_page)
  5. Copy

Flask.register_blueprint() 如下 :

  1. def register_blueprint(self, blueprint, **options):
  2. first_registration = False
  3. if blueprint.name in self.blueprints:
  4. assert self.blueprints[blueprint.name] is blueprint, (
  5. 'A name collision occurred between blueprints %r and %r. Both'
  6. ' share the same name "%s". Blueprints that are created on the'
  7. ' fly need unique names.' % (
  8. blueprint, self.blueprints[blueprint.name], blueprint.name
  9. )
  10. )
  11. else:
  12. # 注册蓝图到Flask应用对象
  13. self.blueprints[blueprint.name] = blueprint
  14. self._blueprint_order.append(blueprint)
  15. first_registration = True
  16. # 将回调在构建蓝图实例时的延迟函数,即deferred_functions中的函数
  17. # 函数为:lambda s: s.add_url_rule(rule, endpoint, view_func, **options)
  18. # s为flask.blueprints.BlueprintSetupState实例
  19. blueprint.register(self, options, first_registration)
  20. Copy

Blueprint.register() 具体如下 :

  1. def register(self, app, options, first_registration=False):
  2. self._got_registered_once = True
  3. # 创建一个flask.blueprints.BlueprintSetupState实例
  4. state = self.make_setup_state(app, options, first_registration)
  5. if self.has_static_folder:
  6. state.add_url_rule(
  7. self.static_url_path + '/<path:filename>',
  8. view_func=self.send_static_file, endpoint='static'
  9. )
  10. for deferred in self.deferred_functions:
  11. # 调用flask.blueprints.BlueprintSetupState实例的add_url_rule方法
  12. deferred(state)
  13. Copy

flask.blueprints.BlueprintSetupState.add_url_rule() 如下 :

  1. def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
  2. if self.url_prefix is not None:
  3. if rule:
  4. rule = '/'.join((
  5. self.url_prefix.rstrip('/'), rule.lstrip('/')))
  6. else:
  7. rule = self.url_prefix
  8. options.setdefault('subdomain', self.subdomain)
  9. if endpoint is None:
  10. endpoint = _endpoint_from_view_func(view_func)
  11. defaults = self.url_defaults
  12. if 'defaults' in options:
  13. defaults = dict(defaults, **options.pop('defaults'))
  14. # 使用flask.Flask.add_url_rule方法
  15. # 第二个参数为endpoint参数,为蓝图名称.视图名称,simple_page.show
  16. self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
  17. view_func, defaults=defaults, **options)
  18. Copy

如果是使用蓝图注册路由 , 那么第一个路由同样是设置静态文件路由 , 在 flask.blueprints.Blueprint.register() 中可见如下内容 :

  1. if self.has_static_folder:
  2. state.add_url_rule(
  3. self.static_url_path + '/<path:filename>',
  4. view_func=self.send_static_file, endpoint='static'
  5. )
  6. Copy

该蓝图注册到应用时 , 路由注册规则如下 :

  1. # 可以在flask.Flask.add_url_rule中
  2. # line 1215:self.url_map.add(rule)下添加一行输出代码:
  3. # print(rule.__repr())
  4. # 随后启动应用,就能得到如下信息
  5. # 我们也可以输出flas.Flask.view_functions属性查看绑定的视图函数,这里就不说明了
  6. <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
  7. <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
  8. <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>
  9. Copy

蓝图还可以在不同的位置挂载 :

  1. app.register_blueprint(simple_page, url_prefix='/pages')
  2. Copy

生成规则如下 :

  1. <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
  2. <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
  3. <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>
  4. Copy

蓝图资源

蓝图也可以提供资源 , 有时候你会只为他提供的资源而引入一个蓝图

蓝图资源文件夹

我们可以通过访问 Blueprint 对象的 root_path 属性来访问蓝图资源文件夹 :

  1. >>> simple_page.root_path
  2. '/Users/username/TestProject/myapplication'
  3. Copy

并且你可以使用 open_response() 函数快速获取文件资源 :

  1. with simple_page.open_resource('static/style.css') as f:
  2. code = f.read()
  3. Copy

静态文件

Flask 一样 , Blueprint 可以通过 static_folder 关键字参数提供一个指向文件系统上文件夹的路径 , 这可以是一个绝对路径 , 也可以是相对于蓝图文件夹的相对路径 :

  1. admin = Blueprint('admin', __name__, static_folder='static')
  2. Copy

模板

同样的 , 蓝图也提供模板 :

  1. admin = Blueprint('admin', __name__, template_folder='templates')
  2. Copy

总而言之 , 蓝图相当于 Flask 应用实例下的 "Flask" 应用实例 (“子应用”) , 它能将你的项目理想化
对于 Blueprint 对象中的方法 , 你不妨看看 , 也许有你想要的功能

参考
https://lyonyang.github.io/blogs/05-Web%E6%A1%86%E6%9E%B6/Flask/06-Flask%20-%20%E6%BA%90%E7%A0%81%E4%B9%8B%E8%93%9D%E5%9B%BE.html