Flask初探五( Blueprint / url_for / endpoint )

Blueprint (藍(lán)圖)

Blueprints are the recommended way to implement larger or more
pluggable applications in Flask 0.7 and later.

Blueprint 是為了更方便實(shí)現(xiàn)模塊化開發(fā)而誕生的.

模塊化

為什么要模塊化?

在一個py 文件中寫成百上千或者更多個接口, 相信大部分或者絕大部分人都不愿意維護(hù)這樣的代碼, 其次所有接口都寫在一個文件中不利于團(tuán)隊(duì)協(xié)作. 基于以上兩點(diǎn),分模塊 / 團(tuán)隊(duì)協(xié)作 的模塊化就顯的很有必要了.

Blueprint 與模塊

一般來說,都會以功能進(jìn)行模塊劃分, 這里就不討論如何劃分了. 假設(shè)有首頁 / 新聞頁 / 登錄 三個模塊.

原始寫法

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'index'


@app.route('/news')
def index():
    return 'index'


@app.route('/login')
def index():
    return 'index'


if __name__ == '__main__':
    app.run(debug=True)

接口通過app.route 將規(guī)則 和 視圖函數(shù)進(jìn)行綁定, 存在同名函數(shù),綁定失敗

**導(dǎo)入Blueprint **

# 導(dǎo)入Blueprint
from flask import Blueprint
from flask import Flask

# 實(shí)例化Blueprint 對象
#                       藍(lán)圖名, import_name ,規(guī)則前綴
index_blue = Blueprint("index", __name__, url_prefix="/index")
news_blue = Blueprint("news", __name__, url_prefix="/news")
login_blue = Blueprint("login", __name__, url_prefix="/login")

app = Flask(__name__)


# 用Blueprint 對象實(shí)現(xiàn)接口
@index_blue.route('/')
def index():
    return 'index'


@news_blue.route('/')
def index():
    return 'news'


@login_blue.route('/')
def index():
    return 'index'


# 注冊Blueprint 
app.register_blueprint(index_blue)
app.register_blueprint(news_blue)
app.register_blueprint(login_blue)

if __name__ == '__main__':
    print(app.url_map)
    app.run(debug=True)

運(yùn)行結(jié)果

Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
 <Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
 <Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
 <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])

Blueprint 的使用步驟

  • 實(shí)例化Blueprint 對象, 需要導(dǎo)入Blueprint 類
  • 用Blueprint 對象實(shí)現(xiàn)實(shí)現(xiàn)接口
  • 注冊Blueprint

雖然用Blueprint 實(shí)現(xiàn)了接口,但是接口依然在一個文件中, 接下來進(jìn)行拆分

模塊化

01-模塊化后的項(xiàng)目目錄.png

main.py

from flask import Flask

from modules import index_blue, news_blue, login_blue

app = Flask(__name__)

# 注冊藍(lán)圖
app.register_blueprint(index_blue)
app.register_blueprint(news_blue)
app.register_blueprint(login_blue)

if __name__ == '__main__':
    print(app.url_map)
    app.run(debug=True)

__init__.py

# 導(dǎo)入Blueprint
from flask import Blueprint

# 實(shí)例化藍(lán)圖對象
index_blue = Blueprint("index", __name__, url_prefix="/index")
news_blue = Blueprint("news", __name__, url_prefix="/news")
login_blue = Blueprint("login", __name__, url_prefix="/login")

from . import index
from . import news
from . import login

index.py

from . import index_blue


@index_blue.route('/')
def index():
    return 'index'

login.py

from . import login_blue


@login_blue.route('/')
def index():
    return 'login'

news.py

from . import news_blue


@news_blue.route('/')
def index():
    return 'news'

運(yùn)行結(jié)果

Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
 <Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
 <Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
 <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])

藍(lán)圖的作用原理

猜測: 未使用藍(lán)圖時, url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index> 這樣的Rule 對象使用藍(lán)圖之后,url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index.index> 這樣的Rule 對象.
所以藍(lán)圖是通過改變視圖函數(shù)的端點(diǎn)的方式區(qū)分視圖函數(shù)的.

endpoint
flask初探二 中簡單的探究了endpoint, 這里將探究藍(lán)圖與endpoint 之間的關(guān)系

藍(lán)圖的route

    def route(self, rule, **options):
        """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the
        :func:`url_for` function is prefixed with the name of the blueprint.
        """
        def decorator(f):
            endpoint = options.pop("endpoint", f.__name__)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

和 flask 的route 基本一樣

藍(lán)圖的add_url_rule

    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
        the :func:`url_for` function is prefixed with the name of the blueprint.
        """
        if endpoint:
            assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
        self.record(lambda s:
            s.add_url_rule(rule, endpoint, view_func, **options))

BlueprintSetupState 的add_url_rule

   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'))
        # 重點(diǎn)在這
        self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
                              view_func, defaults=defaults, **options)

循著源碼的運(yùn)行,可以看到藍(lán)圖最終是通過調(diào)用 app.add_url_rule 的方式將規(guī)則和視圖函數(shù)進(jìn)行的綁定, 區(qū)別是在調(diào)用的時候?qū)ndpoint 進(jìn)行的傳值, 從而印證了我們開始的猜測.

結(jié)論

  • 藍(lán)圖是通過改變endpoint 的方式區(qū)分視圖函數(shù)的.
  • endpoint 默認(rèn)情況下是視圖函數(shù)名,
  • 使用藍(lán)圖后, endpoint 為 藍(lán)圖名.視圖函數(shù)名

endpoint 與 url_for

url_for

def url_for(endpoint, **values):
...(省略)...

從url_for 方法的定義可以看出, url_for需要的是endpoint

  • 在不使用藍(lán)圖的情況下, 可以直接使用函數(shù)名字符串得到URL
@app.route("/")
def index():
    pass

url_for("index")
  • 使用藍(lán)圖時, 使用 藍(lán)圖名.視圖函數(shù)名 字符串得到URL

login_blue = Blueprint("login", __name__, url_prefix="/login")

@login_blue.route('/')
def index():
    return 'login'

url_for("login.index") # 得到 /login/

到此結(jié)? DragonFangQy 2018.6.30

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容