[源碼分析]flask藍圖實現原理分析

看這篇文章以前,建議看一下我以前寫的: flask中路由匹配是如何實現的

BluePrint(藍圖)的概念說白了就是路由組,全部註冊到該藍圖上的路由都使用同一個前綴。這樣方便了管理,不一樣的功能能夠放在一個模塊(好比admin模塊)中實現,更加解耦。html

首先來看看藍圖是如何使用的:python

# 定義一個藍圖
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

看起來跟常見的定義視圖函數的方式同樣,只不過在添加路由的時候,須要以藍圖開頭。app

來看看源碼中是如何實現的。函數

藍圖的功能是在flask 0.7版本中被加入的,app在調用 register_blueprint 方法的時候會調用 Blueprint 類中的 register 方法來註冊該藍圖中添加的全部路由。url

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來看看它裏面有什麼:code

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

返回了一個類。先無論。來看看deferred_functions是什麼,從名字上能夠看出是延遲函數之類的。htm

來梳理一下流程,app.register_blueprint 註冊藍圖以後,會激活Buleprint類中的register方法,在register方法中循環調用 deferred_functions 中的函數來執行,咱們大概能猜出來這段代碼的功能就是將藍圖中定義的路由都添加到路由組中。blog

以上面的藍圖例子,

@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中。

個人blog: https://blog.shiniao.fun/

相關文章
相關標籤/搜索