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([ index.index>,
  login.index>,
  news.index>,
 ' (HEAD, GET, OPTIONS) -> static>])

Blueprint 的使用步骤

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

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

模块化

Flask初探五( Blueprint / url_for / endpoint )_第1张图片
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([ index.index>,
  login.index>,
  news.index>,
 ' (HEAD, GET, OPTIONS) -> static>])

蓝图的作用原理

猜测: 未使用蓝图时, url_map 存放的是形如 index> 这样的Rule 对象使用蓝图之后,url_map 存放的是形如 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

你可能感兴趣的:(Flask初探五( Blueprint / url_for / endpoint ))