4. 蓝图与模块化设计
说实话,我第一次接触 Flask 的时候,也犯过把所有路由写在一个文件里的毛病。那时候项目小,也就几十个接口,觉得挺爽的。直到有一天,我打开那个 app.py 文件,发现它已经膨胀到 2000 多行……
嗯,从那以后,我就再也不敢这么干了。
你想想看,一个配方管理系统,如果所有路由、模型、配置都堆在一个文件里,后期维护简直就是噩梦。这时候,蓝图(Blueprint) 就是来救场的。
为什么需要蓝图?
蓝图说白了,就是 Flask 提供的一种「模块化」方案。它允许你把不同功能的路由、视图、静态文件、模板组织成独立的模块,然后再注册到主应用中。
我个人习惯把蓝图理解为「小应用」。它有自己的路由前缀、模板文件夹、静态文件路径,但它不是独立运行的,必须挂载到主应用上。
举个例子,配方管理系统的核心功能无非就是:
- 配方管理:增删改查配方
- 原料管理:管理原料库存、单位
- 分类管理:对配方或原料进行分类
这三个功能之间虽然有数据关联,但逻辑上是独立的。把它们拆成三个蓝图,每个蓝图只关心自己的事,代码清晰度直接拉满。
- 解耦:每个模块独立开发、独立测试
- 复用:蓝图可以跨项目复用(比如用户认证蓝图)
- 可维护:修改配方模块不会影响原料模块
- 团队协作:不同成员可以同时开发不同蓝图
蓝图注册与路由分发
蓝图的使用其实就三步:定义蓝图 → 编写路由 → 注册到应用。我带你走一遍流程。
1. 定义蓝图
在 app/recipes/ 目录下创建 __init__.py,定义蓝图对象:
from flask import Blueprint
# 第一个参数是蓝图名称,第二个参数是导入名(通常用 __name__)
# url_prefix 是路由前缀,所有该蓝图下的路由都会自动加上 /recipes
recipes_bp = Blueprint('recipes', __name__, url_prefix='/recipes')
2. 编写路由
在 app/recipes/views.py 中编写视图函数:
from . import recipes_bp
@recipes_bp.route('/')
def list_recipes():
return "返回所有配方列表"
@recipes_bp.route('/<int:recipe_id>')
def get_recipe(recipe_id):
return f"返回配方 {recipe_id} 的详细信息"
@recipes_bp.route('/create', methods=['POST'])
def create_recipe():
return "创建新配方"
3. 注册到主应用
在 app/__init__.py 或 app.py 中注册蓝图:
from flask import Flask
from app.recipes import recipes_bp
def create_app():
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(recipes_bp)
return app
这样,所有 /recipes/ 开头的请求都会被分发到 recipes_bp 处理。路由分发逻辑清晰,一目了然。
template_folder 和 static_folder,这样每个蓝图可以有自己的模板和静态文件,互不干扰。
将配方、原料、分类拆分为独立模块
好,理论讲完了,咱们直接动手。下面是我在项目中常用的目录结构:
recipe_manager/
├── app/
│ ├── __init__.py # 应用工厂
│ ├── recipes/ # 配方模块
│ │ ├── __init__.py # 定义蓝图
│ │ ├── views.py # 路由视图
│ │ ├── models.py # 数据模型
│ │ └── forms.py # 表单(如果有)
│ ├── ingredients/ # 原料模块
│ │ ├── __init__.py
│ │ ├── views.py
│ │ ├── models.py
│ │ └── forms.py
│ ├── categories/ # 分类模块
│ │ ├── __init__.py
│ │ ├── views.py
│ │ ├── models.py
│ │ └── forms.py
│ └── templates/ # 全局模板
│ ├── base.html
│ └── ...
├── config.py
└── run.py
每个模块内部结构一致,开发起来非常顺手。下面我分别展示三个蓝图的定义。
配方蓝图(recipes)
# app/recipes/__init__.py
from flask import Blueprint
recipes_bp = Blueprint(
'recipes',
__name__,
url_prefix='/recipes',
template_folder='templates',
static_folder='static'
)
原料蓝图(ingredients)
# app/ingredients/__init__.py
from flask import Blueprint
ingredients_bp = Blueprint(
'ingredients',
__name__,
url_prefix='/ingredients',
template_folder='templates',
static_folder='static'
)
分类蓝图(categories)
# app/categories/__init__.py
from flask import Blueprint
categories_bp = Blueprint(
'categories',
__name__,
url_prefix='/categories',
template_folder='templates',
static_folder='static'
)
注册所有蓝图
# app/__init__.py
from flask import Flask
from app.recipes import recipes_bp
from app.ingredients import ingredients_bp
from app.categories import categories_bp
def create_app():
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(recipes_bp)
app.register_blueprint(ingredients_bp)
app.register_blueprint(categories_bp)
return app
这样,三个模块就完全独立了。每个模块的开发者只需要关注自己的蓝图,不需要关心其他模块的路由冲突。
url_prefix,结果路由冲突,请求总是跑到错误的视图上。记住,每个蓝图的 url_prefix 必须是唯一的。
蓝图模块化结构图
下面这张图展示了蓝图模块化的核心逻辑:
蓝图 vs 传统单文件路由
为了让你更直观地感受蓝图的优势,我整理了一个对比表格:
| 对比维度 | 单文件路由 | 蓝图模块化 |
|---|---|---|
| 代码组织 | 所有路由堆在一个文件 | 按功能拆分为独立模块 |
| 路由前缀 | 手动拼接 URL 前缀 | 蓝图自动处理 url_prefix |
| 团队协作 | 容易产生合并冲突 | 各模块独立开发,互不影响 |
| 可测试性 | 需要启动整个应用才能测试 | 可以单独测试每个蓝图 |
| 复用性 | 几乎无法复用 | 蓝图可以跨项目复用 |
我个人强烈建议,从项目一开始就采用蓝图模块化设计。哪怕你现在觉得项目小、没必要,等后面功能一多,你就知道蓝图的好了。
recipes/views/ 下再分 list.py、detail.py、create.py 等。这样每个文件只负责一个功能点,查找和修改都特别快。