逐步理解 Flask 的 Blueprint(蓝本)

Python 的 Flask 框架能让我们快速的建立一个轻量级的 Web 或 REST API。对于小应用由一个 @app 装饰一撸到底就行,当项目稍具规模或要更清晰就要考虑模块化,于是来到了我们今天的话题,首先是

为什么需要 Blueprint?

比如说我们一定超级简单的 Flask 应用 main.py 的代码如下:

现在需要加上一个后端管理 admin 模块,可以继续在 main.py 中加代码,并且依然用 @app 来装饰

这只是用 URL 前缀来区分了功能,分别是

  1. GET /
  2. POST /admin/users

再进一步, 我们可在同一个模块中使用 Blueprint,main.py 的代码变为

实现的功能与前面的 main.py 是一样的。从这里高亮行可以看出实现一个 Blueprint 需要基本的三步

  1. 声明一个 Blueprint
  2. 用 Blueprint 实例去装饰函数
  3. 注册 Blueprint 到 Flask 应用实例上

由 Blueprint 装饰的函数,访问时必须加上它的 url_prefix 前缀。如

@admin.route('/user', methods=['POST'])

访问时要加上 /admin 前缀,就是

POST /admin/users

注:有两种方式指定 Blueprint 的 url_prefix

  1. 声明 Blueprint 时可以指定 url_prefix: 相当于是默认的 url_prefix
  2. 向 app 注册 Blueprint 时也可以指定 url_prefix:注册时不指定就用声明时的 url_prefix, 否则会覆盖声明时的 url_prefix

Blueprint 的 url_prefix 就像是 Java 的 Spring MVC 中 Controller 类的 @RequestMapping 定义一样,定义在类级别的 @RequestMapping path 会作为 Controller 方法的 URL 前缀。

使用 Blueprint 模块化应用

上一节中我们体验了在同一个 Python 使用 Blueprint, 虽然 Blueprint 是通了,但那样不能算作真正的模块化。要实现模块化至少得把功能分到不同的 Python 文件中去,最好是有各自的目录。也就产生了两种使用 Blueprint 模块化的方式,使用模块和包

Blueprint 定义在不同的模块中

首先进行文件分家,我们要把 /admin 下的实现挪到  admin.py  文件中去,于是 main.pyadmin.py 的内容就变成了

admin.py

在 admin 中像使用 @app 一样用 @admin 装饰,并且不需要知道哪个 Flask 应用去注册它

main.py

注册第二行的引用是从模块 admin 中引用同名的  admin 变量

Blueprint 定义在不同的包(目录) 中

对于更复杂的应用,把不同功能分布到不同的模块文件中还不够,用包(目录)来组织不同的功能就显得十分的必要了。

如果在 admin 中只有一个 user 模块使用了 Blueprint,我们可以用下面的目录结构

my-project
├── admin
│   └── user.py
└── main.py

在 user.py 中

在 main.py 中就是

现在两个 API

  1. GET /
  2. POST /admin/users

再往 admin 包中添加一个  order 模块,情形就变得稍微复杂了,由 order 和  user 都必须使用同一个 Blueprint 实例,所以需要在 __init__.py 中初始化它。同时为了在 Flask 启动时加载 admin/user 和 admin/order 模块,也就要在 __init__.py 中导入它们(main 导入 admin 包时还要立即导入其下的  user 和  order 模块)。所以,重新组织后的目录结构如下

my-project
├── admin
│   ├── __init__.py
│   ├── order.py
│   └── user.py
└── main.py

每个文件的内容如下

admin/order.py

admin/user.py

admin/__init__.py

这个文件的内容必须在声明 Blueprint 后把使用了它的模块导入到命包空间来,否则不能注册那些 endpoints。其目的就是当我们在 main.py 中用

from admin import admin

导入 admin 包时能立即导入 admin 包中的 user 和 order 模块

如果不想在 admin/__init__.py 中逐个导入子模块,而需要把所有子模块全部自动引入的话,就在 __init__.py 中加入下方的代码

或者

IDE 可能会提示后面的  import 语句要提到前面去,这时候不能听它的,否则出现错误

ImportError: cannot import name 'admin' from partially initialized module 'admin' (most likely due to a circular import)

main.py

main.py 文件没什么变化,关键的部分应该是在 admin/__init__.py 中

现在运行  Flask,就有以下三个 API

  1. GET /
  2. GET /admin/orders
  3. POST /admin/users

关于  Flask Blueprint 的其他知识

更多的关于 Blueprint 的用法可参考源码中的 Bluepint 初始函数和 Flask.register_blueprint 注册函数。

由于 Blueprint 相当于一个 Flask 应用的子站点,所以它可以定义自己的静态文件映射,子域名。Blueprint 可以嵌套,也就是一个 Blueprint 还能注册另一个 Blueprint

那么 blueprint_bb 中的 /user 的 API 完整路径就是

/aa/bb/user

我们或许还可以考虑把 Flask app 和 Blueprint 实例作为参数传递给别的模块使用。

链接:

  1. Modular Applications with Blueprints
  2. Use a Flask Blueprint to Architect Your Applications

本文链接 https://yanbin.blog/understand-flask-blueprint/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments