django dev&prod数据库更改策略规范——如何保证本地测试的数据库修改能安全上线

在 Django 项目中,本地开发阶段产生大量 migrations 文件并不是问题
真正的风险在于:

数据库变更是否能在 预发 / 生产环境中安全、可控、可回滚地执行

本文总结了一套 经过大量 Django 项目验证的数据库变更策略规范,适用于个人项目和团队协作,可直接作为团队开发守则使用。

一、核心原则(必须遵守)

1️⃣ 生产环境只允许做两件事

  • 拉取代码(包含 migrations 文件
  • 执行:
python manage.py migrate

2️⃣ 严禁在生产环境手动改表结构

任何形式的手动 ALTER TABLE 都会导致:

  • 本地 / 线上数据库状态漂移(schema drift)
  • 后续 migration 执行失败
  • 数据库状态不可追溯

👉 数据库结构的唯一事实来源:Git 中的 migration 文件

3️⃣ 已经上线执行过的 migration 文件,永远不要修改

  • ❌ 不要改字段
  • ❌ 不要改依赖
  • ❌ 不要改顺序

如果需要修正错误:

新建一个 migration 来修

二、本地一堆 migrations,发布时怎么保证线上安全?

标准做法(推荐)

  1. 本地开发完成后: python manage.py makemigrations python manage.py migrate
  2. 所有 migration 文件提交到 Git
  3. 发布到线上后执行: 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

重点关注:

  • 是否有删字段
  • 是否有字段类型变更
  • 是否有大表操作

四、预发 / 灰度环境是关键缓冲区

推荐发布流程

  1. 使用与线上完全一致的构建产物
  2. 部署到 staging
  3. 执行: python manage.py migrate
  4. 跑冒烟 / 回归测试(尤其是涉及数据迁移的功能)
  5. 确认无误后再发布线上

⚠️ 不要让“开发者本地数据库状态”成为事实来源


五、多人协作中的 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 一次性迁移大量数据

八、推荐的低/零停机数据库变更策略

✅ 新增字段(两步走)

  1. 新增字段,允许 NULL
  2. 代码兼容新旧字段
  3. 后台任务 / 脚本分批回填数据
  4. 再次 migration:改成 NOT NULL / 加约束

✅ 修改字段类型(三步走)

  1. 新增新字段(新类型)
  2. 双写 + 分批迁移数据
  3. 切换读写到新字段
  4. 删除旧字段

✅ 加索引

  • 大表索引需要特别谨慎
  • 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
  • 大表变更拆步骤、可回滚