Flask Buleprint 源码浅析

802 阅读2分钟

通常的使用Buleprint搭配RestFul API的方式使用

user = Buleprint("user",__name__)
_api = API(user)
_api.add_resouce(User,"/")

步骤一:
首先创建Buleprint
传入Buleprint实例化API,查看API代码

class Api(object):
    def __init__(self,**):
        if app is not None:
            self.app = app
            self.init_app(app)
    def init_app(self, app):
        # If app is a blueprint, defer the initialization
        try:
            app.record(self._deferred_blueprint_init)
        # Flask.Blueprint has a 'record' attribute, Flask.Api does not
        except AttributeError:
            self._init_app(app)
        else:
            self.blueprint = app

步骤二:
这里的app此时是buleprint,接着注册resource

def add_resource(self, resource, *urls, **kwargs):
    if self.app is not None:
        self._register_view(self.app, resource, *urls, **kwargs)
    else:
        self.resources.append((resource, urls, kwargs))

步骤三:
调用_register_view()

 def _register_view(self, app, resource, *urls, **kwargs):
    for url in urls:
        # If this Api has a blueprint
        if self.blueprint:
            # And this Api has been setup
            if self.blueprint_setup:
                # Set the rule to a string directly, as the blueprint is already
                # set up.
                self.blueprint_setup.add_url_rule(url, view_func=resource_func, **kwargs)
                continue
            else:
                # Set the rule to a function that expects the blueprint prefix
                # to construct the final url.  Allows deferment of url finalization
                # in the case that the associated Blueprint has not yet been
                # registered to an application, so we can wait for the registration
                # prefix
                rule = partial(self._complete_url, url)
        else:
            # If we've got no Blueprint, just build a url with no prefix
            rule = self._complete_url(url, '')
        # Add the url to the application or blueprint
        app.add_url_rule(rule, view_func=resource_func, **kwargs)

步骤四:
这里的appbuleprint是相等的,后面调用buleprintadd_url_rule方法进行url注册

 def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
    
def record(self, func):
    self.deferred_functions.append(func)

步骤五:
这里将ruleview_func使用匿名函数封装添加到deferred_functions列表中去。
至此,关于user的蓝本设置完毕。接下来就是注册蓝本到全局url中去。

business_modules = [
    "app.user:user"
]
def register_blueprints(app, blueprints: list):
    for bp in blueprints:
        module = import_string(bp)
        app.register_blueprint(module, url_prefix="/" + bp.split(":")[1])

步骤六:
在这里,创建flask应用app的时候,调用register_buleprint注册蓝本。这里的module就是前面所创建的user(Buleprint的实例化)

 def register_blueprint(self, blueprint, **options):
    blueprint.register(self, options, first_registration)

步骤七:
调用Buleprintregister进行注册。这里的selfflask应用app

def register(self, app, options, first_registration=False):
    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)

这里的state暂时不用管,下面会说到。在register方法中,可以看到这段代码。

for deferred in self.deferred_functions:
        deferred(state)

其中self.deferred_functions是最开始实例化API的时候步骤四中生成的(匿名函数),在匿名函数中可以看见是调用了state.add_url_rule去注册url的。state是什么呢?

def make_setup_state(self, app, options, first_registration=False):
    return BlueprintSetupState(self, app, options, first_registration)

可以看出stateBlueprintSetupState的实例,查看其add_url_rule方法。

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    if self.url_prefix is not None:
        if rule:
            rule = '/'.join((
                self.url_prefix.rstrip('/'), rule.lstrip('/')))
        else:
            rule = self.url_prefix
    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)

这里可以看出最后调用了appadd_url_rule进行注册的(这里也就是app注册路由的方法)