建立一個藍圖json
from flask import Blueprint food = Blueprint('food', __name__)
Blueprint接受兩個參數:藍本的名字和藍本所在的包或模塊,注意第一個參數, 後面會用到。這裏建立了一個名爲food的藍圖flask
將該藍圖註冊到app中app
from .food import food as food_blueprint app.register_blueprint(food_blueprint, url_prefix='/food')
經過藍圖註冊路由dom
@food.route('/', methods=['GET', 'POST']) def index(): pass
class Blueprint(_PackageBoundObject): warn_on_modifications = False _got_registered_once = False json_encoder = None json_decoder = None import_name = None template_folder = None root_path = None def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None): _PackageBoundObject.__init__(self, import_name, template_folder, root_path=root_path) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain self.static_folder = static_folder self.static_url_path = static_url_path self.deferred_functions = [] if url_defaults is None: url_defaults = {} self.url_values_defaults = url_defaults
__init__方法是屬性綁定ide
接着看app.register_blueprint方法函數
class Flask(_PackageBoundObject): @setupmethod def register_blueprint(self, blueprint, **options): first_registration = False if blueprint.name in self.blueprints: assert self.blueprints[blueprint.name] is blueprint, ( 'A name collision occurred between blueprints %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)
app.register_blueprint方法簡單的檢測了註冊的藍圖是否在app中已存在以後,調用了藍圖的register方法ui
class Blueprint(_PackageBoundObject): def register(self, app, options, first_registration=False): """Called by :meth:`Flask.register_blueprint` to register all views and callbacks registered on the blueprint with the application. Creates a :class:`.BlueprintSetupState` and calls each :meth:`record` callback with it. :param app: The application this blueprint is being registered with. :param options: Keyword arguments forwarded from :meth:`~Flask.register_blueprint`. :param first_registration: Whether this is the first time this blueprint has been registered on the application. """ 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 deferred_functions:
deferred(state)
先生成state,state是BlueprintSetupState實例,記錄了藍圖註冊是的各類狀態信息。this
class BlueprintSetupState(object): def __init__(self, blueprint, app, options, first_registration): #: a reference to the current application self.app = app #: a reference to the blueprint that created this setup state. self.blueprint = blueprint #: a dictionary with all options that were passed to the #: :meth:`~flask.Flask.register_blueprint` method. self.options = options #: as blueprints can be registered multiple times with the #: application and not everything wants to be registered #: multiple times on it, this attribute can be used to figure #: out if the blueprint was registered in the past already. self.first_registration = first_registration subdomain = self.options.get('subdomain') if subdomain is None: subdomain = self.blueprint.subdomain #: The subdomain that the blueprint should be active for, ``None`` #: otherwise. self.subdomain = subdomain url_prefix = self.options.get('url_prefix') if url_prefix is None: url_prefix = self.blueprint.url_prefix #: The prefix that should be used for all URLs defined on the #: blueprint. self.url_prefix = url_prefix #: A dictionary with URL defaults that is added to each and every #: URL that was defined with the blueprint. self.url_defaults = dict(self.blueprint.url_values_defaults) self.url_defaults.update(self.options.get('url_defaults', ())) 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)
請注意url_prefix屬性,它在後面經過藍圖註冊路由的時候有用url
回到藍圖的register方法,生成state後,依次調用deferred_functions屬性中註冊的函數。spa
class Blueprint(_PackageBoundObject):
def record(self, func): """Registers a function that is called when the blueprint is registered on the application. This function is called with the state as argument as returned by the :meth:`make_setup_state` method. """ if self._got_registered_once and self.warn_on_modifications: from warnings import warn warn(Warning('The blueprint was already registered once ' 'but is getting modified now. These changes ' 'will not show up.')) self.deferred_functions.append(func) 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 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" if view_func: assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots" self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
deferred_functions列表在實例初始化的時候是一個空列表,經過record方法向deferred_functions添加函數。
當咱們經過@blueprint.route這個帶參數的裝飾器定義路由的時候,經過上面的代碼能夠看到,裝飾器經過endpoint = options.pop("endpoint", f.__name__)語句找到了endpoint,若是options中指定了endpoint則使用指定的,不然使用被裝飾函數的名稱做爲endpoint。 self.add_url_rule(rule, endpoint, f, **options)語句調用了add_url_rule方法,觀察add_url_rule方法發現經過record註冊函數。到此即可知道咱們經過藍圖註冊全部路由state.add_url_rule方法註冊到app中。
回到前面BlueprintSetupState中的add_url_rule方法,rule = self.url_prefix + rule代表會在每一個rule以前添加url_prefix,這個url_prefix就是前面註冊藍圖時指定的。這就是咱們實現網頁分類功能的基礎,對於不一樣的類別定義不一樣的藍圖,註冊藍圖的時候指定不一樣的url_prefix,這樣不一樣類別的網頁自動的加上了各自的前綴。
下面是app註冊路由的函數,能夠發如今flask中,rule和endpoint關聯(),endpoint和view_func關聯(經過字典實現)
@setupmethod def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): if endpoint is None: endpoint = _endpoint_from_view_func(view_func) options['endpoint'] = endpoint methods = options.pop('methods', None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: methods = getattr(view_func, 'methods', None) or ('GET',) if isinstance(methods, string_types): raise TypeError('Allowed methods have to be iterables of strings, ' 'for example: @app.route(..., methods=["POST"])') methods = set(item.upper() for item in methods) # Methods that should always be added required_methods = set(getattr(view_func, 'required_methods', ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. if provide_automatic_options is None: provide_automatic_options = getattr(view_func, 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: provide_automatic_options = True required_methods.add('OPTIONS') else: provide_automatic_options = False # Add the required methods now. methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule) if view_func is not None: old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: raise AssertionError('View function mapping is overwriting an ' 'existing endpoint function: %s' % endpoint) self.view_functions[endpoint] = view_func