在 Django 项目中,本地开发阶段产生大量 migrations 文件并不是问题。
真正的风险在于:
数据库变更是否能在 预发 / 生产环境中安全、可控、可回滚地执行。
本文总结了一套 经过大量 Django 项目验证的数据库变更策略规范,适用于个人项目和团队协作,可直接作为团队开发守则使用。
一、核心原则(必须遵守)
1️⃣ 生产环境只允许做两件事
- 拉取代码(包含 migrations 文件)
- 执行:
python manage.py migrate
2️⃣ 严禁在生产环境手动改表结构
任何形式的手动 ALTER TABLE 都会导致:
- 本地 / 线上数据库状态漂移(schema drift)
- 后续 migration 执行失败
- 数据库状态不可追溯
👉 数据库结构的唯一事实来源:Git 中的 migration 文件
3️⃣ 已经上线执行过的 migration 文件,永远不要修改
- ❌ 不要改字段
- ❌ 不要改依赖
- ❌ 不要改顺序
如果需要修正错误:
✅ 新建一个 migration 来修
二、本地一堆 migrations,发布时怎么保证线上安全?
标准做法(推荐)
- 本地开发完成后:
python manage.py makemigrations python manage.py migrate - 将 所有 migration 文件提交到 Git
- 发布到线上后执行:
python manage.py migrate
Django 会根据 django_migrations 表判断:
- 哪些 migration 已执行
- 哪些是新增的
👉 只执行未执行过的迁移
这是 Django migration 机制的核心设计。
三、发布前的强制检查清单(建议放进 CI)
✅ 1. 检查是否有未生成的 migration
python manage.py makemigrations --check --dry-run
- 非 0 退出 → 直接阻断发布
✅ 2. 确保迁移在干净数据库能跑通
在 CI / staging 环境执行:
python manage.py migrate
python manage.py test
✅ 3. 审查迁移计划(防止误操作)
python manage.py migrate --plan
重点关注:
- 是否有删字段
- 是否有字段类型变更
- 是否有大表操作
四、预发 / 灰度环境是关键缓冲区
推荐发布流程
- 使用与线上完全一致的构建产物
- 部署到 staging
- 执行:
python manage.py migrate - 跑冒烟 / 回归测试(尤其是涉及数据迁移的功能)
- 确认无误后再发布线上
⚠️ 不要让“开发者本地数据库状态”成为事实来源
五、多人协作中的 migration 冲突处理
常见问题
- 两个人在同一个 app 下都生成了
0005_xxx.py
正确处理方式
python manage.py makemigrations --merge
Django 会生成一个 merge migration 来合并依赖。
团队协作建议
- 一个 PR 对应一组 migrations
- 合并前 rebase
- 解决 migration 编号冲突后再合并
六、要不要合并(squash) migrations?
结论:可以,但不是必须
适合 squash 的场景:
- 项目运行半年 / 一年以上
- 单个 app migration 文件过多,影响维护
- 新环境初始化迁移速度明显变慢
使用方式
python manage.py squashmigrations your_app 0001 0050
⚠️ 注意事项:
- squash 后通常需要保留旧 migration 一段时间
- 这是 维护性优化,不是发布安全的必要条件
七、线上“安全更改”的真正难点:大表 & 锁表风险
即使 migrate 流程正确,SQL 本身也可能导致线上事故。
高风险操作列表
- 给大表新增 NOT NULL 字段
- 修改字段类型
- 给大表加索引
- RunPython 一次性迁移大量数据
八、推荐的低/零停机数据库变更策略
✅ 新增字段(两步走)
- 新增字段,允许
NULL - 代码兼容新旧字段
- 后台任务 / 脚本分批回填数据
- 再次 migration:改成
NOT NULL/ 加约束
✅ 修改字段类型(三步走)
- 新增新字段(新类型)
- 双写 + 分批迁移数据
- 切换读写到新字段
- 删除旧字段
✅ 加索引
- 大表索引需要特别谨慎
- PostgreSQL 建议使用 CONCURRENTLY
- Django 原生不一定默认支持,需要自定义 Migration
九、数据迁移(RunPython)的最佳实践
强烈建议
- 提供
reverse_code,保证可逆 - 不要一次性处理百万级数据
- migration 只做轻逻辑
- 重数据迁移用:
- 后台任务
- 管理命令
- 离线脚本
十、线上发布的标准操作流程
常规发布
python manage.py migrate
python manage.py collectstatic --noinput
# 滚动重启服务
有风险的 schema 变更
- 发布前确保备份或可回滚方案
- 必须 staging 全流程演练
- 低峰期执行或灰度发布
⚠️ 数据库回滚往往比代码回滚困难得多
删字段 / 改类型前一定要想清楚回退路径。
十一、可直接采用的团队数据库规范(推荐)
✅ migrations 必须进 Git
✅ CI 必须 makemigrations --check
✅ staging 必须先 migrate + 冒烟
✅ 线上已执行 migration 不允许修改
✅ 大表变更必须拆分(兼容 → 回填 → 收口)
✅ 大数据迁移不用 migration 一次性跑完
结语
Django 的 migration 机制本身已经非常成熟
出问题的,往往不是框架,而是流程和规范。
只要你做到:
- 数据库结构由 migration 唯一驱动
- 每次发布严格走 staging → prod
- 大表变更拆步骤、可回滚