欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flask初探五( Blueprint / url_for / endpoint )

程序员文章站 2022-07-15 10:08:50
...

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 实现了接口,但是接口依然在一个文件中, 接下来进行拆分

模块化

Flask初探五( Blueprint / url_for / endpoint )

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