
Django squashmigrations 后 makemigrations 仍报告字段变更,本质是模型状态与压缩迁移文件未对齐——压缩仅合并历史SQL逻辑,不重置模型与迁移的“当前快照”一致性;需通过清空历史+重建初始迁移并配合 --fake-initial 安全修复。
django `squashmigrations` 后 `makemigrations` 仍报告字段变更,本质是模型状态与压缩迁移文件未对齐——压缩仅合并历史sql逻辑,不重置模型与迁移的“当前快照”一致性;需通过清空历史+重建初始迁移并配合 `--fake-initial` 安全修复。
? 问题根源:squashmigrations 不等于“重置迁移起点”
python manage.py squashmigrations mainapp 0244 的作用是*将 0001 到 0244 共 244 个迁移逻辑合并为一个新迁移文件(如 `0001_squashed0244.py)**,但它**不会修改你的当前模型定义(models.py`)**,也不会自动将该压缩文件标记为“已完全反映当前模型状态”。
关键点在于:
- 压缩迁移文件中的
operations是从原 244 个迁移中提取、去重、排序后的历史操作集合; - 但 Django 在运行
makemigrations时,会将当前models.py的实际字段定义与数据库当前结构(或最后一次成功应用的迁移所描述的结构)进行比对; - 若你在此前曾手动修改过
models.py(例如调整了max_length、添加了blank=True、变更了related_name等),而这些修改未被原始 0244 迁移完整覆盖(比如在最后几个迁移中遗漏、或被--fake过跳过),那么压缩后的迁移文件就无法准确描述“模型 → 数据库”的最终映射关系; - 结果就是:
showmigrations显示压缩迁移为[ - ](未应用),但migrate拒绝执行(因无待应用变更),而makemigrations --dry-run却检测到差异——说明 Django 认为“模型 ≠ 当前数据库状态”,矛盾由此产生。
✅ 简单说:
squashmigrations是“归档旧账”,不是“刷新账本”。模型和数据库之间的“一致性契约”仍需你显式重建。
? 正确修复流程(推荐:安全、可复现、符合 Django 最佳实践)
以下步骤适用于开发/测试环境(严禁直接用于生产环境,必须先备份数据库!):
✅ 步骤 1:伪造回滚至零点(清空迁移记录)
python manage.py migrate mainapp zero --fake
⚠️ 此命令不改动数据库表结构,仅在
django_migrations表中删除mainapp下所有迁移记录,告诉 Django:“这些迁移我假装都没执行过”。
Django Unfold下载提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
✅ 步骤 2:彻底清理旧迁移文件(保留 __init__.py!)
# 进入 mainapp/migrations/ rm -f mainapp/migrations/0*.py # 确保 __init__.py 仍存在(否则 Django 不识别 migrations 目录) ls mainapp/migrations/__init__.py # 应输出存在
✅ 步骤 3:基于当前模型生成全新初始迁移
python manage.py makemigrations mainapp
✅ 此时生成的
0001_initial.py将精确反映你当前models.py的全部定义(包括所有字段类型、约束、关系等),不再依赖历史碎片。
✅ 步骤 4:伪造应用初始迁移(关键!)
python manage.py migrate mainapp --fake-initial
✅
--fake-initial是 Django 专为此场景设计的安全机制:它要求——
- 数据库中已存在与
0001_initial.py中CreateModel/AlterField等操作完全匹配的表结构(即你压缩前的数据库已是最终形态);- 且该迁移文件含
initial = True标志(makemigrations自动生成时默认包含)。
→ Django 仅向django_migrations插入一条记录,不做任何 DDL 操作,完美跳过重复建表风险。
验证结果:
python manage.py showmigrations mainapp # 输出应为: # mainapp # [X] 0001_initial
⚠️ 重要注意事项与避坑指南
| 风险点 | 说明 | 正确做法 |
|---|---|---|
| 生产环境误操作 | 直接 --fake-initial 可能导致结构错位 |
✅ 生产环境必须先用 sqlmigrate mainapp 0001_initial 导出 SQL,人工核对与线上表结构是否 100% 一致;必要时先备份再执行 |
忽略 __init__.py |
删除后 makemigrations 无法识别 migrations 目录 |
✅ 清理时 rm *.py 但绝不删 __init__.py
|
| 多人协作未同步 | 团队成员未统一执行上述流程 | ✅ 将新生成的 0001_initial.py 提交至 Git,并在 PR 中明确标注“迁移已压缩,全员需执行 migrate --fake-initial” |
混淆 --fake 和 --fake-initial |
--fake 用于跳过任意迁移;--fake-initial 仅限首次接入已有数据库
|
✅ 本场景必须用 --fake-initial,不可替换为 --fake
|
? 总结:何时该压缩?何时该重来?
- ✅ 适合
squashmigrations:项目早期(RunPython、团队模型高度一致、仅需减小迁移文件数量。 - ✅ 必须重走
zero → makemigrations → --fake-initial:压缩后出现模型差异、涉及数据迁移逻辑、多人分支长期未同步、或你已不确定数据库真实结构来源。
最终原则:Django 迁移的权威性,永远建立在
models.py、迁移文件、数据库三者严格一致之上。 压缩是优化手段,不是修复手段;当一致性破裂,重建才是最稳健的工程选择。











