app.route定义的装饰器只有在app实例创建后才能运行。如果需要将app之前是由route来添加路由规则和功能或者将路由和app接耦合,在这种场景下Blueprint提供很好的解决方法。在蓝本定义的路由规则处于休眠状态,直到这个蓝图注册到app中。
蓝图定义:
auth = Blueprint('auth',__name__) @auth.route('/logout') def logout(): pass
这里完成的工作只是创建一个蓝图并将路由加入蓝图中
from .auth import auth as auth_blueprint app.register_blueprint(auth_blueprint,url_prefix='/auth')
这里将蓝图中注册的路由添加到app实例中。下面来分析下蓝图的工作原理:
auth.route('/logout')装饰器的源码如下
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
route调用add_url_rule函数,add_url_rule函数其实是调用record函数将下面的一个匿名函数对象传入到deferred_functions的列表中,这个在register将调用注册这些匿名函数。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 endpoints should not contain dots" self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
到此蓝图中注册路由就完成了。接下来是注册实现的原理
首先调用Flask对象的register_blueprint函数,这个函数调用blueprint的register处理处理蓝图的注册。
def register_blueprint(self, blueprint, **options): """Registers a blueprint on the application. .. versionadded:: 0.7 """ first_registration = False if blueprint.name in self.blueprints: assert self.blueprints[blueprint.name] is blueprint, \ 'A blueprint\'s name collision occurred between %r and ' \ '%r. Both share the same name "%s". Blueprints that ' \ 'are created on the fly need unique names.' % \ (blueprint, self.blueprints[blueprint.name], blueprint.name) else: self.blueprints[blueprint.name] = blueprint self._blueprint_order.append(blueprint) first_registration = True blueprint.register(self, options, first_registration)
blueprint的register函数调完成了一下功能首先make_setup_state创建了BlueprintSetupState实例。然后将这个实例作为deferred_functions中存储的匿名函数的入参。调用的BlueprintSetupState的add_rule_url函数,而在
BlueprintSetupState的add_rule_url函数其实最终调用的flask的add_rule_url
blueprint的register函数
def register(self, app, options, first_registration=False): """Called by :meth:`Flask.register_blueprint` to register a blueprint on the application. This can be overridden to customize the register behavior. Keyword arguments from :func:`~flask.Flask.register_blueprint` are directly forwarded to this method in the `options` dictionary. """ self._got_registered_once = True state = self.make_setup_state(app, options, first_registration) if self.has_static_folder: state.add_url_rule(self.static_url_path + '/<path:filename>', view_func=self.send_static_file, endpoint='static') for deferred in self.deferred_functions: deferred(state)
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)