文章列表
2 分钟阅读

Django 数据库迁移实战:makemigrations 和 migrate 背后的坑

更新说明:新增 Django 数据库迁移实践。


系列:Python Web 工程实践

第 4 / 8 篇

  1. Python Web 项目配置管理:环境变量、.env 和生产配置怎么拆
  2. Python Web 项目如何连接数据库:SQLAlchemy、Django ORM、迁移怎么选
  3. Alembic 数据库迁移入门:表结构变更怎么安全上线
  4. Django 数据库迁移实战:makemigrations 和 migrate 背后的坑
  5. FastAPI 项目测试指南:pytest、TestClient、依赖覆盖怎么用
  6. Django 测试入门:Model、View、API 测试怎么写
  7. FastAPI 分层架构:router、service、repository 怎么拆
  8. PostgreSQL 基础:索引、事务、慢查询怎么理解

Django 的迁移很好用,也很容易让人放松警惕。makemigrations 能生成文件,migrate 能执行,但这不代表每一次结构变化都适合直接上线。

如果你刚开始用 Django,可以先看 Django 安装与使用指南Django 进阶使用指南

迁移文件记录的是变化过程

模型定义是当前状态,迁移文件记录的是从旧状态变到新状态的步骤。

Terminal window
python manage.py makemigrations
python manage.py migrate

生成后不要急着提交,先看文件内容。

blog/migrations/0003_article_status.py
operations = [
migrations.AddField(
model_name="article",
name="status",
field=models.CharField(default="draft", max_length=20),
),
]

这个迁移会给旧数据补默认值。小表没问题,大表就要考虑执行时间和锁。

字段改名要格外小心

Django 有时能识别字段重命名,有时会生成“删除旧字段 + 新增字段”。如果不看迁移文件,数据可能直接没了。

危险的迁移长这样:

migrations.RemoveField(model_name="article", name="name")
migrations.AddField(model_name="article", name="title", field=models.CharField(max_length=120))

如果确实是改名,应该生成或手动调整为:

migrations.RenameField(
model_name="article",
old_name="name",
new_name="title",
)

大表新增字段怎么做

对大表新增非空字段,我更倾向于拆成几步:

  1. 先新增可为空字段
  2. 发布代码开始写入新字段
  3. 后台脚本补齐历史数据
  4. 再改成非空约束

这样比“一次迁移完成所有事”更稳。

status = models.CharField(max_length=20, null=True, blank=True)

等数据补齐后,再改成:

status = models.CharField(max_length=20, default="draft")

常用检查命令

查看迁移计划:

Terminal window
python manage.py showmigrations
python manage.py migrate --plan

只生成迁移但不执行:

Terminal window
python manage.py makemigrations --dry-run --verbosity 3

检查模型和迁移是否一致:

Terminal window
python manage.py makemigrations --check --dry-run

提交迁移文件的习惯

我会把模型改动和迁移文件放在同一个提交里。提交前至少确认:

  • 迁移文件名能看出目的
  • 没有误删字段
  • 默认值不会让大表迁移太慢
  • 回滚路径不是空的
  • 测试库能完整执行

总结

Django 迁移不是负担,它是线上数据结构的历史记录。认真看迁移文件,比事后修数据便宜得多。尤其是字段改名、大表默认值和删除操作,不要只相信自动生成结果。