makemigrations --merge 能直接用,但仅适用于简单冲突场景(如两分支各自新增不同字段),此时生成空合并迁移;若涉及同字段修改、删/加同名字段或含runpython,则会静默失败或导致迁移中断、记录不全。

迁移文件冲突时,makemigrations --merge 能不能直接用
能,但只适用于简单场景:比如两个分支各自新增了一个字段,没动同一张表的同一字段,也没删模型或改字段类型。Django 会生成一个空迁移(如 0003_merge_20260507_2300.py),里面只有 dependencies 合并,没有 operations。这种情况下跑 python manage.py migrate 就能过。
但一旦出现以下情况,--merge 会静默失败或生成错误迁移:
- 两个分支都改了同一个字段的
max_length,但值不同 - 一个分支删了字段,另一个加了同名字段
- 有
RunPython或自定义数据迁移逻辑
此时 showmigrations 可能显示 “conflicted”,但不会报错;真正出问题往往在 migrate 执行中途——表结构卡在半途、django_migrations 记录不全、后续迁移全失效。
手动删 django_migrations 表记录是否安全
不安全,尤其对 auth、contenttypes、admin 这类 Django 内置应用。它们的迁移有强依赖顺序,比如 auth.0012_alter_user_first_name_max_length 明确依赖 auth.0011_update_proxy_permissions。如果只删了 0012 的记录,留下 0011,Django 下次 run migrate 会直接抛 InconsistentMigrationHistory 错误,而不是继续执行。
真要删,必须满足三个条件:
- 确认该迁移确实没在数据库里生效(用
sqlmigrate app_name 00xx看 SQL,再手动查表结构验证) - 该迁移不包含不可逆操作(如
DeleteModel、RemoveField) - 你同时删掉它之后所有未执行的迁移记录(不能只删中间一条)
否则不如重来:清空 migrations/(留 __init__.py),删库,再 makemigrations + migrate。
--fake 和 --fake-initial 的区别与风险点
--fake 是“我保证这个迁移的 SQL 已经手动执行过了”,Django 只往 django_migrations 插一条记录,不做任何数据库操作。适合修复漏执行但结构已对齐的情况。
--fake-initial 是特例:仅用于已有数据库首次接入 Django。它要求当前数据库结构**完全匹配**某个初始迁移(通常是 0001_initial.py)所描述的状态,且该迁移必须带 initial = True 标记。不是用来绕过报错的补丁。
常见误用:
- 对非 initial 迁移用
--fake-initial→ 报错ValueError: Cannot fake an initial migration that is not marked as initial - 对 SQLite 用
--fake-initial却忘了检查db_table名是否和模型里一致 → 表存在但 Django 找不到 - 在多人协作项目里擅自
--fake某个迁移 → 其他人拉代码后migrate会试图建已存在的表,直接崩
SQLite 下迁移冲突更难察觉的几个原因
SQLite 没有服务端进程,所有锁靠文件系统实现。这导致三类隐蔽问题:
-
DATABASES['default']['NAME']是相对路径(如db.sqlite3),但你在不同目录下运行manage.py,实际操作的是不同文件 ——showmigrations显示已执行,其实只是另一个db.sqlite3的状态 - 开发时用 IDE 自带终端、命令行终端、VS Code 集成终端切换运行,可能触发文件锁残留,
migrate读到旧的django_migrations缓存 - 内存数据库(
sqlite://:memory:)每次重启就清空,但迁移记录还在 Python 进程里缓存,造成“刚迁完就报 no such table”
查证方式很简单:ls -la db.sqlite3 看修改时间;用 sqlite3 db.sqlite3 ".tables" 直接看真实表名;确认 settings.py 里路径是绝对路径或始终从项目根目录运行命令。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











