蓝图(Blueprint) 是 Flask 中用于模块化组织代码的核心对象。它本质上是一个“未注册的应用”,包含了一组路由、错误处理器、模板和静态文件配置,但只有在被主应用(app)注册后才会生效。
理解 Blueprint 对象的 API,能帮你更好地实现代码解耦和复用。
🏗️ 1. 创建蓝图对象
蓝图的构造函数非常灵活,允许你定义其资源路径和前缀。
from flask import Blueprint
# 基本用法
my_bp = Blueprint('name', __name__)
# 高级用法:指定独立的模板和静态文件夹
admin_bp = Blueprint(
'admin',
__name__,
template_folder='templates', # 默认为 'templates'
static_folder='static', # 默认为 'static'
static_url_path='/admin_static', # URL 访问前缀,默认为 /static
url_prefix='/admin' # 路由前缀,注册时可覆盖
)参数详解:
name: 蓝图的唯一标识名,用于url_for('name.endpoint')。import_name: 通常是__name__,用于定位资源根目录。template_folder: 相对于蓝图所在目录的模板文件夹路径。static_folder: 相对于蓝图所在目录的静态文件夹路径。static_url_path: 浏览器访问静态文件的 URL 路径。url_prefix: 最常用。给该蓝图下所有路由自动加上前缀(如/api/v1)。
🛠️ 2. 核心 API 方法
蓝图对象拥有与 Flask 应用对象几乎相同的装饰器和注册方法。
A. 路由注册
@bp.route(rule, **options): 注册视图函数。bp.add_url_rule(rule, endpoint=None, view_func=None, **options): 底层注册方法。
B. 错误处理
@bp.errorhandler(code_or_exception): 注册仅对该蓝图生效的错误处理器。- 注意:如果蓝图中没有定义该错误的处理器,Flask 会 fallback 到主应用的处理器。
C. 生命周期钩子
@bp.before_request: 仅在该蓝图的路由被调用前执行。@bp.after_request: 仅在该蓝图的路由执行后执行。@bp.teardown_request: 请求结束时执行。@bp.before_app_request: 全局钩子。即使定义在蓝图中,也会对整个应用的所有请求生效。@bp.after_app_request: 全局钩子。对整个应用的所有响应生效。
D. 模板与上下文
@bp.context_processor: 注入模板变量,仅对该蓝图的模板生效。@bp.app_context_processor: 注入模板变量,对整个应用的模板生效。@bp.template_filter(name): 注册仅在该蓝图中可用的 Jinja2 过滤器。@bp.template_global(): 注册全局可用的模板函数/变量。
E. 静态文件
- 蓝图自动拥有一个名为
<blueprint_name>.static的端点。- 用法:
url_for('admin.static', filename='style.css')
- 用法:
🔌 3. 注册蓝图到应用
蓝图本身不能运行,必须通过 app.register_blueprint() 挂载到主应用上。
app.register_blueprint(
my_bp,
url_prefix='/api', # 可选:覆盖或补充蓝图定义的前缀
subdomain='api', # 可选:绑定特定子域名
name_prefix='v1_' # 可选:给所有端点名称加前缀 (Flask 2.0+)
)💡 4. 关键概念:端点名称 (Endpoint Name)
这是新手最容易混淆的地方。每个视图函数都有一个唯一的“端点名”,默认等于函数名。
- 主应用中的视图:端点名为
function_name。 - 蓝图中的视图:端点名为
blueprint_name.function_name。
示例:
# main_bp.py
@main_bp.route('/')
def index():
pass
# auth_bp.py
@auth_bp.route('/login')
def login():
pass在模板或代码中使用 url_for 时:
- 跳转主页:
url_for('main.index') - 跳转登录:
url_for('auth.login')
技巧:如果你在蓝图内部想引用当前蓝图的其他路由,可以使用相对引用
.endpoint_name(例如url_for('.login')),Flask 会自动补全蓝图名前缀。
⚠️ 5. 常见陷阱与最佳实践
A. 循环导入 (Circular Import)
问题:app.py 导入 blueprint,而 blueprint 又导入 app 或 models(依赖 app)。
解决:
- 将蓝图实例 (
bp = Blueprint(...)) 放在单独的__init__.py或views.py顶部。 - 在
__init__.py的最后导入routes模块。 - 使用
current_app代替直接导入app。
B. 静态文件路径冲突
如果多个蓝图都使用默认的 static_folder='static',它们的静态文件 URL 可能会冲突或被主应用的 /static 覆盖。
建议:为每个蓝图设置独特的 static_url_path,或者统一使用主应用的静态文件夹。
C. 模板查找顺序
当渲染模板时,Flask 的查找顺序是:
- 主应用的
templates/文件夹。 - 已注册蓝图的
templates/文件夹(按注册顺序)。
建议:为了避免命名冲突,建议在蓝图模板文件夹内再建一层子目录,如templates/admin/dashboard.html,并在渲染时使用完整路径render_template('admin/dashboard.html')。
📝 总结:Blueprint vs App API 对比
| 功能 | Flask App (app) | Blueprint (bp) | 备注 |
|---|---|---|---|
| 路由 | @app.route | @bp.route | 语法完全一致 |
| 错误处理 | @app.errorhandler | @bp.errorhandler | 蓝图优先,找不到则找 App |
| 前置钩子 | @app.before_request | @bp.before_request | 蓝图钩子只影响蓝图路由 |
| 全局钩子 | N/A | @bp.before_app_request | 蓝图也能定义影响全局的钩子 |
| 配置 | app.config | 无独立配置 | 蓝图共享 current_app.config |
| 运行 | app.run() | ❌ 不可运行 | 蓝图只是组件 |
掌握了 Blueprint API,你就拥有了构建大型、可维护 Flask 项目的基石。接下来,你可以尝试将一个单体应用拆分为 auth, api, main 三个蓝图,体验模块化的魅力!