Flask初探五( Blueprint / url_for / endpoint )

Blueprint (蓝图)

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

Blueprint 是为了更方便实现模块化开发而诞生的.

模块化

为什么要模块化?

在一个py 文件中写成百上千或者更多个接口, 相信大部分或者绝大部分人都不愿意维护这样的代码, 其次所有接口都写在一个文件中不利于团队协作. 基于以上两点,分模块 / 团队协作 的模块化就显的很有必要了.

Blueprint 与模块

一般来说,都会以功能进行模块划分, 这里就不讨论如何划分了. 假设有首页 / 新闻页 / 登录 三个模块.

原始写法

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 将规则 和 视图函数进行绑定, 存在同名函数,绑定失败

**导入Blueprint **

# 导入Blueprint
from flask import Blueprint
from flask import Flask

# 实例化Blueprint 对象
#                       蓝图名, import_name ,规则前缀
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 对象实现接口
@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)

运行结果

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 的使用步骤

  • 实例化Blueprint 对象, 需要导入Blueprint 类
  • 用Blueprint 对象实现实现接口
  • 注册Blueprint

虽然用Blueprint 实现了接口,但是接口依然在一个文件中, 接下来进行拆分

模块化

01-模块化后的项目目录.png

main.py

from flask import Flask

from modules import index_blue, news_blue, login_blue

app = Flask(__name__)

# 注册蓝图
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

# 导入Blueprint
from flask import Blueprint

# 实例化蓝图对象
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'

运行结果

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>])

蓝图的作用原理

猜测: 未使用蓝图时, url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index> 这样的Rule 对象使用蓝图之后,url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index.index> 这样的Rule 对象.
所以蓝图是通过改变视图函数的端点的方式区分视图函数的.

endpoint
flask初探二 中简单的探究了endpoint, 这里将探究蓝图与endpoint 之间的关系

蓝图的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 基本一样

蓝图的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'))
        # 重点在这
        self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
                              view_func, defaults=defaults, **options)

循着源码的运行,可以看到蓝图最终是通过调用 app.add_url_rule 的方式将规则和视图函数进行的绑定, 区别是在调用的时候对endpoint 进行的传值, 从而印证了我们开始的猜测.

结论

  • 蓝图是通过改变endpoint 的方式区分视图函数的.
  • endpoint 默认情况下是视图函数名,
  • 使用蓝图后, endpoint 为 蓝图名.视图函数名

endpoint 与 url_for

url_for

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

从url_for 方法的定义可以看出, url_for需要的是endpoint

  • 在不使用蓝图的情况下, 可以直接使用函数名字符串得到URL
@app.route("/")
def index():
    pass

url_for("index")
  • 使用蓝图时, 使用 蓝图名.视图函数名 字符串得到URL

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

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

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

到此结  DragonFangQy 2018.6.30

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容