逐步理解 Flask 的 Blueprint(蓝本)
Python 的 Flask 框架能让我们快速的建立一个轻量级的 Web 或 REST API。对于小应用由一个 @app 装饰一撸到底就行,当项目稍具规模或要更清晰就要考虑模块化,于是来到了我们今天的话题,首先是
现在需要加上一个后端管理 admin 模块,可以继续在 main.py 中加代码,并且依然用 @app 来装饰
这只是用 URL 前缀来区分了功能,分别是
再进一步, 我们可在同一个模块中使用 Blueprint,main.py 的代码变为
实现的功能与前面的 main.py 是一样的。从这里高亮行可以看出实现一个 Blueprint 需要基本的三步
由 Blueprint 装饰的函数,访问时必须加上它的
Blueprint 的 url_prefix 就像是 Java 的 Spring MVC 中 Controller 类的 @RequestMapping 定义一样,定义在类级别的 @RequestMapping path 会作为 Controller 方法的 URL 前缀。
admin.py
在 admin 中像使用 @app 一样用 @admin 装饰,并且不需要知道哪个 Flask 应用去注册它
main.py
注册第二行的引用是从模块 admin 中引用同名的 admin 变量
如果在 admin 中只有一个 user 模块使用了 Blueprint,我们可以用下面的目录结构
在 main.py 中就是
现在两个 API
再往 admin 包中添加一个 order 模块,情形就变得稍微复杂了,由 order 和 user 都必须使用同一个 Blueprint 实例,所以需要在 __init__.py 中初始化它。同时为了在 Flask 启动时加载 admin/user 和 admin/order 模块,也就要在 __init__.py 中导入它们(main 导入 admin 包时还要立即导入其下的 user 和 order 模块)。所以,重新组织后的目录结构如下
admin/order.py
admin/user.py
admin/__init__.py
这个文件的内容必须在声明 Blueprint 后把使用了它的模块导入到命包空间来,否则不能注册那些 endpoints。其目的就是当我们在 main.py 中用
如果不想在 admin/__init__.py 中逐个导入子模块,而需要把所有子模块全部自动引入的话,就在 __init__.py 中加入下方的代码
或者
IDE 可能会提示后面的
main.py 文件没什么变化,关键的部分应该是在 admin/__init__.py 中
现在运行 Flask,就有以下三个 API
由于 Blueprint 相当于一个 Flask 应用的子站点,所以它可以定义自己的静态文件映射,子域名。Blueprint 可以嵌套,也就是一个 Blueprint 还能注册另一个 Blueprint
那么 blueprint_bb 中的 /user 的 API 完整路径就是
链接:
永久链接 https://yanbin.blog/understand-flask-blueprint/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
为什么需要 Blueprint?
比如说我们一定超级简单的 Flask 应用 main.py 的代码如下:1from flask import Flask
2
3app = Flask(__name__)
4
5
6@app.route('/', methods=['GET'])
7def hello():
8 return "hello world!"现在需要加上一个后端管理 admin 模块,可以继续在 main.py 中加代码,并且依然用 @app 来装饰
1from flask import Flask
2
3app = Flask(__name__)
4
5
6@app.route('/admin/user', methods=['POST'])
7def add_user():
8 return 'add user'
9
10
11@app.route('/', methods=['GET'])
12def hello():
13 return "hello world!"这只是用 URL 前缀来区分了功能,分别是
- GET /
- POST /admin/users
再进一步, 我们可在同一个模块中使用 Blueprint,main.py 的代码变为
1from flask import Flask, Blueprint
2
3app = Flask(__name__)
4admin = Blueprint('admin_blueprint', __name__)
5
6
7@admin.route('/user', methods=['POST'])
8def add_user():
9 return 'add user'
10
11
12@app.route('/', methods=['GET'])
13def hello():
14 return "hello world!"
15
16
17app.register_blueprint(admin, url_prefix='/admin')实现的功能与前面的 main.py 是一样的。从这里高亮行可以看出实现一个 Blueprint 需要基本的三步
- 声明一个 Blueprint
- 用 Blueprint 实例去装饰函数
- 注册 Blueprint 到 Flask 应用实例上
由 Blueprint 装饰的函数,访问时必须加上它的
url_prefix 前缀。如@admin.route('/user', methods=['POST'])访问时要加上
/admin 前缀,就是POST /admin/users注:有两种方式指定 Blueprint 的 url_prefix
- 声明 Blueprint 时可以指定 url_prefix: 相当于是默认的 url_prefix
- 向 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.py 和 admin.py 的内容就变成了admin.py
1from flask import Blueprint
2
3admin = Blueprint('admin_blueprint', __name__)
4
5
6@admin.route('/users', methods=['POST'])
7def add_user():
8 return 'add user'在 admin 中像使用 @app 一样用 @admin 装饰,并且不需要知道哪个 Flask 应用去注册它
main.py
1from flask import Flask
2from admin import admin
3
4app = Flask(__name__)
5
6
7@app.route('/', methods=['GET'])
8def hello():
9 return "hello world!"
10
11
12app.register_blueprint(admin, url_prefix='/admin')注册第二行的引用是从模块 admin 中引用同名的 admin 变量
Blueprint 定义在不同的包(目录) 中
对于更复杂的应用,把不同功能分布到不同的模块文件中还不够,用包(目录)来组织不同的功能就显得十分的必要了。如果在 admin 中只有一个 user 模块使用了 Blueprint,我们可以用下面的目录结构
my-project在 user.py 中
├── admin
│ └── user.py
└── main.py
1from flask import Blueprint
2
3admin = Blueprint('admin', __name__)
4
5
6@admin.route('/users', methods=['POST'])
7def add_user():
8 return 'add user'在 main.py 中就是
1from flask import Flask
2from admin.user import admin
3
4app = Flask(__name__)
5
6
7@app.route('/', methods=['GET'])
8def hello():
9 return "hello world!"
10
11
12app.register_blueprint(admin, url_prefix='/admin')现在两个 API
- GET /
- 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
1from admin import admin
2
3
4@admin.route('/orders', methods=['GET'])
5def list_orders():
6 return 'all orders'admin/user.py
1from admin import admin
2
3
4@admin.route('/users', methods=['POST'])
5def add_user():
6 return 'add user'admin/__init__.py
1from flask import Blueprint
2
3admin = Blueprint('admin', __name__)
4
5from . import user
6from . import order这个文件的内容必须在声明 Blueprint 后把使用了它的模块导入到命包空间来,否则不能注册那些 endpoints。其目的就是当我们在 main.py 中用
from admin import admin导入 admin 包时能立即导入 admin 包中的 user 和 order 模块
如果不想在 admin/__init__.py 中逐个导入子模块,而需要把所有子模块全部自动引入的话,就在 __init__.py 中加入下方的代码
1import pkgutil
2for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
3 _module = loader.find_module(module_name).load_module(module_name)
4 globals()[module_name] = _module或者
1import importlib
2import pkgutil
3
4for loader, module_name, is_pkg in pkgutil.walk_packages(__path__, __name__ + '.'):
5 importlib.import_module(module_name)IDE 可能会提示后面的
import 语句要提到前面去,这时候不能听它的,否则出现错误ImportError: cannot import name 'admin' from partially initialized module 'admin' (most likely due to a circular import)main.py
1from flask import Flask
2from admin import admin
3
4app = Flask(__name__)
5
6
7@app.route('/', methods=['GET'])
8def hello():
9 return "hello world!"
10
11
12app.register_blueprint(admin, url_prefix='/admin')main.py 文件没什么变化,关键的部分应该是在 admin/__init__.py 中
现在运行 Flask,就有以下三个 API
- GET /
- GET /admin/orders
- POST /admin/users
关于 Flask Blueprint 的其他知识
更多的关于 Blueprint 的用法可参考源码中的 Bluepint 初始函数和 Flask.register_blueprint 注册函数。由于 Blueprint 相当于一个 Flask 应用的子站点,所以它可以定义自己的静态文件映射,子域名。Blueprint 可以嵌套,也就是一个 Blueprint 还能注册另一个 Blueprint
1blueprint_aa = Blueprint("aa", __name__)
2blueprint_bb = Blueprint("aa", __name__)
3blueprint_aa.register_blueprint(bb, url_prefix="/bb")
4
5app.register_blueprint(blueprint_aa, url_prefix="/aa")那么 blueprint_bb 中的 /user 的 API 完整路径就是
/aa/bb/user我们或许还可以考虑把 Flask app 和 Blueprint 实例作为参数传递给别的模块使用。
链接:
永久链接 https://yanbin.blog/understand-flask-blueprint/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。