蓝图(Blueprints) 是 Flask 实现模块化开发的核心机制。
当一个 Flask 项目变得越来越大,把所有的路由、视图函数、模板都塞进一个 app.py 文件会导致代码难以维护。蓝图允许你将应用拆分成多个独立的组件(模块),最后再将这些组件“注册”到主应用中。
你可以把主应用(App)想象成一家总公司,而蓝图(Blueprint)就是旗下的分公司(如:人力资源部、财务部、技术部)。每个分公司有自己的职能和规则,但都归属于总公司管理。
🏗️ 1. 为什么需要蓝图?
- 代码解耦:将不同功能的代码(如用户认证、博客文章、API 接口)分开存放。
- 易于协作:不同的开发者可以负责不同的蓝图,减少代码冲突。
- URL 前缀管理:可以轻松为整个模块添加 URL 前缀(例如所有 API 接口都以
/api/v1开头)。 - 独立资源:每个蓝图可以拥有自己独立的模板文件夹和静态文件文件夹。
🛠️ 2. 如何创建和使用蓝图?
假设我们要构建一个简单的博客系统,包含两个模块:
- main:主页、关于页面。
- auth:登录、注册。
第一步:目录结构
my_project/
├── app.py # 主应用入口
├── main/ # 主模块蓝图
│ ├── __init__.py # 定义蓝图
│ └── routes.py # 主模块路由
└── auth/ # 认证模块蓝图
├── __init__.py # 定义蓝图
└── routes.py # 认证模块路由第二步:定义蓝图 (main/__init__.py)
from flask import Blueprint
# 创建蓝图实例
# 'main' 是蓝图名称
# __name__ 帮助 Flask 定位资源路径
main_bp = Blueprint('main', __name__)
# 注意:这里不要导入 routes,否则会造成循环引用
# 我们在下面单独导入
from . import routes 第三步:编写路由 (main/routes.py)
from . import main_bp # 从当前包导入蓝图
@main_bp.route('/')
def index():
return '这是主页 (Main Blueprint)'
@main_bp.route('/about')
def about():
return '关于我们 (Main Blueprint)'第四步:定义另一个蓝图 (auth/__init__.py & routes.py)
auth/__init__.py:
from flask import Blueprint
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
# url_prefix: 可选参数,给该蓝图下所有路由自动加上 /auth 前缀
from . import routesauth/routes.py:
from . import auth_bp
@auth_bp.route('/login')
def login():
return '登录页面 (Auth Blueprint) -> 实际访问路径: /auth/login'
@auth_bp.route('/register')
def register():
return '注册页面 (Auth Blueprint) -> 实际访问路径: /auth/register'第五步:在主应用中注册蓝图 (app.py)
from flask import Flask
from main import main_bp
from auth import auth_bp
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(main_bp) # 挂载到根路径
app.register_blueprint(auth_bp) # 挂载到 /auth 路径 (因为蓝图定义时指定了 prefix)
if __name__ == '__main__':
app.run(debug=True)🔍 3. 蓝图的关键特性
A. URL 前缀 (url_prefix)
在注册蓝图或创建蓝图时,可以指定前缀。
# 方式 1:创建时指定
api_bp = Blueprint('api', __name__, url_prefix='/api/v1')
# 方式 2:注册时指定
app.register_blueprint(api_bp, url_prefix='/api/v2')如果蓝图中有一个路由 @api_bp.route('/users'),最终访问路径将是 /api/v1/users 或 /api/v2/users。
B. 独立的模板和静态文件
默认情况下,Flask 会在主应用的 templates 和 static 文件夹中查找资源。但蓝图可以拥有自己的资源文件夹。
# 在创建蓝图时指定
admin_bp = Blueprint(
'admin',
__name__,
template_folder='templates', # 蓝图目录下的 templates 文件夹
static_folder='static' # 蓝图目录下的 static 文件夹
)注意:在模板中引用静态文件时,端点名称会变成 蓝图名.static。
<!-- 引用 admin 蓝图的静态文件 -->
<link rel="stylesheet" href="{{ url_for('admin.static', filename='style.css') }}">C. 请求钩子 (Before/After Request)
蓝图也可以定义自己的 before_request 和 after_request,但它们只对该蓝图内的路由生效。
@admin_bp.before_request
def check_admin_permission():
if not session.get('is_admin'):
return redirect(url_for('auth.login'))如果你希望某个钩子对全局生效,必须在主 app 上定义,而不是在蓝图上。
💡 4. 最佳实践:应用工厂 + 蓝图
在大型项目中,通常结合应用工厂模式使用蓝图。
app/__init__.py:
from flask import Flask
from config import Config
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
# 初始化扩展 (db, login_manager 等)
from extensions import db, migrate
db.init_app(app)
migrate.init_app(app, db)
# 注册蓝图
from app.main.routes import main_bp
from app.auth.routes import auth_bp
app.register_blueprint(main_bp)
app.register_blueprint(auth_bp, url_prefix='/auth')
return app⚠️ 常见陷阱
- 循环导入 (Circular Import):
- 错误做法:在
blueprint/__init__.py中直接import routes,而routes.py又import blueprint。 - 正确做法:在
__init__.py的最后导入routes,或者确保routes.py只导入蓝图实例,不导入其他可能依赖 app 的对象。
- 错误做法:在
- 端点名称冲突:
- 每个视图函数都有一个唯一的“端点名称”(默认是函数名)。如果两个蓝图有同名函数(如都有
index()),Flask 会自动处理为blueprint_name.function_name。 - 在使用
url_for()时,跨蓝图跳转必须指定蓝图名前缀:python # 在 main 蓝图中跳转到 auth 蓝图的 login url_for('auth.login')
- 每个视图函数都有一个唯一的“端点名称”(默认是函数名)。如果两个蓝图有同名函数(如都有
📝 总结
| 特性 | 说明 |
|---|---|
| 核心作用 | 模块化组织代码,避免单文件过大 |
| 创建方法 | bp = Blueprint('name', __name__) |
| 注册方法 | app.register_blueprint(bp) |
| URL 前缀 | 可在创建或注册时通过 url_prefix 设置 |
| 资源隔离 | 支持独立的 template_folder 和 static_folder |
| 适用场景 | 任何超过 5-10 个路由的项目都应使用蓝图 |
掌握了蓝图,你的 Flask 项目就具备了支撑中大型应用的结构基础。接下来,你想学习如何连接数据库 (SQLAlchemy) 并在蓝图中操作数据,还是学习如何部署你的 Flask 应用?