[源码分析] Flask 蓝图实现原理分析

avatar
Chaojie

看这篇文章之前,建议看一下我之前写的:[源码分析]Flask 中路由匹配是如何实现的

BluePrint(蓝图)的概念说白了就是路由组,所有注册到该蓝图上的路由都使用同一个前缀。这样方便了管理,不同的功能可以放在一个模块(比如 admin 模块)中实现,更加解耦。

首先来看看蓝图是如何使用的:

# 定义一个蓝图
simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

# 绑定视图函数
@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template('pages/%s.html' % page)
    except TemplateNotFound:
        abort(404)


        # 在主模块中注册路由
app = Flask(__name__)
app.register_blueprint(simple_page)

看上面的例子,首先定义了一个蓝图 simple_page,然后经由这个蓝图来定义路由以及绑定到视图函数上,最后在主模块中,注册这个蓝图即可。

看起来跟常见的定义视图函数的方式一样,只不过在添加路由的时候,需要以蓝图开头。

来看看源码中是如何实现的。

蓝图的功能是在 flask 0.7 版本中被加入的,app 在调用 register_blueprint 方法的时候会调用 Blueprint 类中的 register 方法来注册该蓝图中添加的所有路由。

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

我们看一下 register 方法:

# blueprints.py
def register(self, app, options, first_registration=False):

    ...
    state = self.make_setup_state(app, options, first_registration)

    ...
    for deferred in self.deferred_functions:
        deferred(state)

额,make_setup_state 是个啥,deferred_functions 又是个啥。我们跳到 make_setup_state 来看看它里面有什么:

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

返回了一个类。先不管。来看看 deferred_functions 是什么,从名字上可以看出是延迟函数之类的。

来梳理一下流程,app.register_blueprint 注册蓝图之后,会激活 Buleprint 类中的 register 方法,在 register 方法中循环调用 deferred_functions 中的函数来执行,我们大概能猜出来这段代码的功能就是将蓝图中定义的路由都添加到路由组中。

以上面的蓝图例子,

@simple_page.route('/', defaults={'page': 'index'})

蓝图的 route 方法是这样的:

def route(self, rule, **options):
    def decorator(f):
        self.add_url_rule(rule, f.__name__, f, **options)
        return f
    return decorator

route 方法是个装饰器,实际上调用了 add_url_rule 方法:

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)

在 record 方法中,将 func 添加到了 deferred_functions 列表中,而 add_url_rule 中调用了 record 方法,那么一切就都可以解释了:

register 方法中的这段代码,

state = self.make_setup_state(app, options, first_registration)
...
for deferred in self.deferred_functions:
    deferred(state)

循环 deferred_functionsdeferred_functions 里面是啥?是 lambda,具体来说,就是蓝图中定义的路由和视图函数,我们通过

@simple_page.route('/<page>')

定义路由之后,实际上就是在 deferred_functions 里面添加了一个 lambda,为什么说它是 defer,因为只有在 register 注册的时候才会真正添加到 app 的 url_map 中。

上面代码中的 state 是一个 BlueprintSetupState 示例,这个类里面有一个 add_url_rule 方法,会在全局 app 的 url_map 中添加路由和视图函数。

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),view_func, defaults=defaults, **options)

来梳理一下:

# state 是 BlueprintSetupState 实例
BlueprintSetupState -> state

# deferred_functions 里面是蓝图路由的 lambda
lambda s: s.add_url_rule -> deferred_functions

for deferred in self.deferred_functions:
    deferred(state)

意思就是 lambda 中的 s 被赋值为 state ,然后state.add_url_rule,
这样就执行了app.add_url_rule

这个延迟执行设计的太巧妙了,蓝图中添加的路由规则只有在 register 方法中才真正的被添加到全局的路由 map 中。

© CC BY-NC-SA 4.0 | Chaojie