Flask1.1.4 Werkzeug1.0.1 源码分析:蓝图

日常项目中使用蓝图来分组用户侧和管理侧的api,使用不同的前缀。今天就来一探蓝图背后的原理。
先看使用蓝图的一个例子。

from flask import Flask, Blueprint

# 实例化
app = Flask(__name__)
# 构建蓝图对象
some_blueprint = Blueprint('some', __name__)

# 使用蓝图对象的方法装饰view_function
@some_blueprint.route("/a")
def hello_world():
    return "<p>Hello, World!</p>"

# 将蓝图注册到 flask_app
app.register_blueprint(some_blueprint, url_prefix='/pre')

之前的系列我们已经搞清楚了Flask 将url_rule和view_function 分别保存到 url_map和view_functions 属性中。蓝图只是一种新的给路由分组管理的形式,最终蓝图中的信息肯定还是要以同样的方式保存到同样的地方,我们来研究下其中的过程吧。

先来看下 @some_blueprint.route(“/a”)

class Blueprint(_PackageBoundObject):

    def route(self, rule, **options):
       	# 和flask_app.route()长得一毛一样
        def decorator(f):
        	# endpoint 默认函数名称
            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):
        # 参数是一个 函数!
        # 这个函数闭包了 路由和函数相关的信息
        self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))

    def record(self, func):
    	# self.deferred_functions 是个 []
    	# 此处就是简单的把这个函数保存到蓝图对象中
        self.deferred_functions.append(func)

由上可见,蓝图 简单的将 路由和函数等信息保存了起来,只不过保存的方式比较特别,保存的是这个匿名函数 lambda s: s.add_url_rule(rule, endpoint, view_func, **options) ,后面会看到在将蓝图注册到flask_app的时候,会调用这个函数。

下面来看下 app.register_blueprint(some_blueprint, url_prefix=‘/pre’)

class Flask(_PackageBoundObject):

    @setupmethod
    def register_blueprint(self, blueprint, **options):
        first_registration = False
		# 调用buleprint的方法
		# 第一个参数为 flask_app
        blueprint.register(self, options, first_registration)
class Blueprint(_PackageBoundObject):

    def make_setup_state(self, app, options, first_registration=False):
    	# 第一个参数 Blueprint实例
    	# 第二个参数 flask_app实例
        return BlueprintSetupState(self, app, options, first_registration)

    def register(self, app, options, first_registration=False):
        self._got_registered_once = True
        # state 是一个 BlueprintSetupState实例 下面研究
        state = self.make_setup_state(app, options, first_registration)
		
		# 之前 Blueprint.route() 保存的函数列表 依次调用
        for deferred in self.deferred_functions:
        	# 回忆下 deferred 函数是 lambda s: s.add_url_rule(rule, endpoint, view_func, **options)
        	# 也就是说 deferred(state) 会调用 state.add_url_rule(rule, endpoint, view_func, **options)
            deferred(state)

        cli_resolved_group = options.get("cli_group", self.cli_group)

看下 BlueprintSetupState 类

class BlueprintSetupState(object):

    def __init__(self, blueprint, app, options, first_registration):
        
        self.app = app
        self.blueprint = blueprint
        self.options = options
		
		# 优先 使用 flask_app.register_blueprint() 时传入的 url_prefix
		# 若没传 再使用Blueprint的 url_prefix
        url_prefix = self.options.get("url_prefix")
        if url_prefix is None:
            url_prefix = self.blueprint.url_prefix
        
        self.url_prefix = url_prefix

    # 上面 deferred(state) 就是调用了此方法
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        if self.url_prefix is not None:
            if rule:
            	# 既有 url_prefix 又有 url_rule的情况下 拼接两者
                rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
            else:
                rule = self.url_prefix
        # endpoint 默认用函数名称
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        defaults = self.url_defaults
        # 最终最终 走到了 flask_app.add_url_rule
        # 注意下 endpoint为 {bulprint.name}.{endpoint} 很合理 应当使用全路径避免组内元素重复
        self.app.add_url_rule(
            rule,
            "%s.%s" % (self.blueprint.name, endpoint),
            view_func,
            defaults=defaults,
            **options
        )

总结下:

  1. Blueprint 包含了一组路由的信息,可以使用统一的前缀。里面有个比较有意思的设计就是在 Blueprint.deferred_functions属性中保存的是个 匿名函数对象 lambda s: s.add_url_rule(rule, endpoint, view_func, **options),匿名函数闭包了路由等信息。
  2. flask.register_blueprint() 最终触发了Blueprint.deferred_functions 函数的依次调用,不过并没有直接传入 flask_app,而是使用 BlueprintSetupState 这个 holder class 进行了一些信息的记录和预处理,之后将其作为参数传入。最终,调用 BlueprintSetupState.add_url_rule(),其内部最终调用了 flask_app.add_url_rule()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值