flask无内置mvc,推荐最小可行结构:app.py、config.py、extensions.py、models/、services/、api/(蓝图)、requirements.txt;避免伪mvc目录拆分,业务逻辑下沉至services/,api按版本或调用方隔离而非模型划分。

Flask 里没有内置 MVC,别硬套目录名
Flask 本身不规定结构,app.py 放一个文件里也能跑。所谓 “MVC 目录” 是人按习惯组织的,不是框架强制的。硬照 Django 或 Spring 的 models/、views/、controllers/ 拆,反而容易让路由和业务逻辑脱节。
常见错误现象:ImportError: cannot import name 'User' from 'models.user' —— 实际是循环导入,因为 views.py 导了 models,而 models.py 又导了 db(它在 app.py 里初始化),但 app.py 又导入了 views。
- 把
db、ma(Marshmallow)、login_manager等扩展实例单独提成extensions.py,避免初始化顺序混乱 -
models/下每个文件只定义一类模型(如user.py、post.py),统一由models/__init__.py导出,外部只从models导 - 别建
controllers/文件夹——Flask 没 controller 层;路由函数(@app.route)+ 蓝图(Blueprint)就是入口,业务逻辑应下沉到services/或utils/
推荐的最小可行目录结构(含蓝图为前提)
真实项目一旦超过 3 个端点,就该用蓝图。以下结构经多个中等规模 Flask 项目验证,部署、测试、IDE 跳转都顺:
myflaskapp/
├── app.py # 创建 app、注册蓝图、配置加载
├── config.py # 配置类(DevelopmentConfig, ProductionConfig)
├── extensions.py # 所有扩展实例:db, ma, migrate, jwt 等
├── models/
│ ├── __init__.py # from .user import User; from .post import Post
│ ├── user.py # class User(db.Model): ...
│ └── post.py
├── services/ # 关键!业务逻辑写这里,非 CRUD 封装
│ ├── user_service.py # create_user(), send_welcome_email()
│ └── auth_service.py
├── api/ # 蓝图目录(按功能域分,非按 MVC 层分)
│ ├── __init__.py
│ ├── users.py # bp = Blueprint('users', __name__); @bp.route('/users')
│ └── auth.py
└── requirements.txt
-
api/users.py里只做参数校验、调services.user_service.create_user()、返回响应,不碰db.session -
services/不依赖 Flask 上下文(如request、current_app),方便单元测试 - 如果用
flask-sqlalchemy,models/中的db必须从extensions.py导入,不能自己SQLAlchemy()
什么时候该拆 api/ 为多个蓝图?
不是按“用户管理”“订单管理”这种后台思维拆,而是按「API 版本」或「调用方隔离」来拆。比如内部系统调用和对外 OpenAPI 需要不同鉴权、限流、日志粒度。
- 版本拆分:
api/v1/users.py和api/v2/users.py,分别注册为bp_v1和bp_v2 - 调用方拆分:
api/internal/(供公司内其他服务调用,走 JWT)、api/public/(供前端调用,走 session) - 千万别按模型拆:一个
users/目录下塞routes.py、schema.py、model.py——这又回到伪 MVC,且跨蓝图复用困难
静态文件和模板放哪?别被“MVC”带偏
Flask 默认找 static/ 和 templates/,这两个是固定路径,跟 MVC 无关。复杂点的项目会遇到两个实际问题:
- 前端用 Vite/React,
static/不够用 → 把构建产物目录(如dist/)设为app.static_folder,并关闭 Flask 的静态路由(app.static_url_path = '') - 多套 UI(管理后台 + 用户前台)需要不同模板继承链 → 用
render_template('admin/dashboard.html'),模板目录保持templates/admin/和templates/frontend/并列即可 -
static/css/app.css被缓存导致更新不生效 → 开发期加app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0,生产期用白名单控制Cache-Control
真正容易被忽略的是:模板里大量 {% include 'partials/header.html' %} 时,这些 partials 不应该散落在各蓝图目录里,而应统一放在 templates/partials/,否则蓝图间复用和 IDE 查找全乱。










