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)