Flask之Blueprint

Blueprint

Blueprint继承自_PackageBoundObject类

class Blueprint(_PackageBoundObject):
    warn_on_modifications = False
    _got_registered_once = False

    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

除了Blueprint名称和导入的模块名,其他参数都有默认值,因此Blueprint可以这样初始化:

# -*- coding: utf-8 -*-
from flask import Blueprint

auth = Blueprint("auth", __name__)

import views


Blueprint实例创建之后,需要调用Flask.register_blueprint()进行注册,我们来分析这个函数的处理流程:

    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)
Flask有个列表blueprints,里面保存了所有Blueprint名,注册新的Blueprint时,首先以Blueprint为索引检查Flask列表blueprints,如果存在了,则给出告警;否则,将该Blueprint名称保存到列表blueprjints中,并调用Blueprint.register()进行深入注册。

继续分析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 + '/',
                               view_func=self.send_static_file,
                               endpoint='static')

        for deferred in self.deferred_functions:
            deferred(state)
如果Blueprint有自己的static目录,则把static目录也添加到Flask路由规则中



BlueprintSetupState

这个类是Blueprint和Flask类之间的临时粘合剂。对象变量分别有Blueprint和Flask的引用、可选参数、url前缀等。需要注意的是,这里的可选参数是一般是调用Flask.register_blueprint()时传入的,优先级要高于Blueprint的可选参数

class BlueprintSetupState(object):
    """Temporary holder object for registering a blueprint with the
    application.  An instance of this class is created by the
    :meth:`~flask.Blueprint.make_setup_state` method and later passed
    to all register callback functions.
    """

    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

        self.first_registration = first_registration

        subdomain = self.options.get('subdomain')
        if subdomain is None:
            subdomain = self.blueprint.subdomain
        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)

初始化有几个地方需要注意(下面的可选参数都是调用Flask.register_blueprint()时传进来的):

  • 如果可选参数中存在"url_prefix",则以可选参数为准,否则以Blueprint自带的"url_prefix"为准
  • 如果可选参数中存在"subdomain",则以可选参数为准,否则以Blueprint自带的"subdomain"为准


BlueprintSetupState唯一对外提供的方法是 def add_url_rule(self, rule, endpoint=None, view_func=None, **options),该方法对url做些处理后,最终还是调用Flask的add_url_rule()来添加路由规则。这里我们之间查看Flask.add_url_rule()方法如何处理的:

class Flask(_PackageBoundObject):
    @setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=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.
        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

  1. 如果没有提供endpoint,则获取路由函数的名称作为路由规则的endpoint(这里说明,每个路由规则必然带有一个endpoint)
  2. 如果没有提供可选参数“methods”,则默认以GET方法来装饰,所有方法转换为大写
  3. 生成路由规则rule,并且添加到url_map中
  4. 如果路由规则处理函数不为空,则添加到view_functions字典中,key就是endpoint,value则是路由处理函数





你可能感兴趣的:(python)