makemigrations生成空迁移文件主要因模型中使用了带括号的动态默认值(如datetime.now())或修改了Meta属性;migrate报“Table already exists”需用--fake对齐状态;SQLite不支持多数DDL变更,PostgreSQL要求NOT NULL字段配default。

makemigrations 什么情况下会生成空迁移文件
当你没改 models.py,却执行了 python manage.py makemigrations,结果输出 No changes detected,但还是生成了一个空迁移文件(比如 0002_auto_20240510_1523.py),大概率是模型类里写了动态内容,比如用 default=datetime.now 而不是 default=datetime.now() —— 注意括号。Django 每次运行 makemigrations 都会重新导入模型,如果默认值是函数调用(带括号),它每次看到的都是“当前时间”,于是认为字段变了。
- 正确写法:
default=timezone.now(不带括号,传函数对象)或default=datetime.now(同理) - 错误写法:
default=datetime.now()或default=uuid4()(立即执行,每次导入都不同) - 另一个常见原因:修改了
Meta.ordering、verbose_name等不影响数据库结构的属性,Django 默认不忽略它们——可加--name手动跳过,或在settings.py中设MIGRATION_MODULES后统一管理
migrate 执行时报错 “Table already exists” 怎么办
典型错误信息:django.db.utils.ProgrammingError: relation "myapp_mymodel" already exists。这不是代码问题,而是 Django 的迁移记录表(django_migrations)和实际数据库状态不一致:表已经存在,但对应迁移没被标记为已执行。
- 先确认:查
SELECT * FROM django_migrations WHERE app = 'myapp' AND name = '0001_initial';,看有没有这条记录 - 如果没记录但表存在,说明你手动建过表,或者之前用
sqlmigrate+psql手动执行过 SQL —— 此时该用python manage.py migrate myapp 0001 --fake告诉 Django “这个迁移我假装执行过了” - 如果已有记录却还报错,可能是迁移文件被删过又重建,导致名字重复;此时要删掉新生成的迁移文件,再
makemigrations --empty myapp写个空迁移来对齐状态 - 切忌直接删数据库表重来,尤其在线上环境 ——
--fake是安全前提下的首选操作
如何让 migrate 跳过某个迁移文件
有时候你想临时绕过一个有问题的迁移(比如它依赖外部服务、或本地开发想快速回退),但又不想删文件(怕团队同步出错)。Django 本身不支持“跳过”,但有可控的替代路径。
- 用
python manage.py migrate myapp 0001指定回退到某一步,相当于“停在那”,之后再migrate就从那里继续 - 若想彻底忽略某次迁移(比如它只在测试环境有意义),可在迁移文件开头加判断:
from django.db import migrations def skip_if_not_production(apps, schema_editor): if not settings.DEBUG: # 实际逻辑放这里 pass <p>class Migration(migrations.Migration): dependencies = [...] operations = [ migrations.RunPython(skip_if_not_production, reverse_code=migrations.RunPython.noop), ] - 注意:
RunPython的reverse_code必须显式指定,否则migrate --fake-reverse会失败
SQLite 和 PostgreSQL 在 migrate 上的关键差异
本地用 SQLite、线上用 PostgreSQL 是常见组合,但两者对迁移的支持力度不同,容易在部署时翻车。
- SQLite 不支持多数 DDL 变更:比如不能
ALTER COLUMN改字段类型、不能删字段、不能加非空约束(除非用db_column手动映射)。Django 遇到这类操作会静默降级为“重建表”,但数据可能丢失 —— 所以别在 SQLite 上信migrate的行为 - PostgreSQL 支持原子性迁移,
ALTER TABLE ... ADD COLUMN这类操作能原地执行;但要注意NOT NULL字段必须配default,否则报错cannot create NOT NULL column without default value - 跨数据库迁移前务必跑一次
python manage.py sqlmigrate myapp 0001,看看生成的 SQL 是否符合目标库语法,特别是JSONField、ArrayField这些特有字段
迁移不是“点一下就完事”的黑盒,它的核心其实是三件事:模型定义、迁移文件快照、数据库当前状态。哪一环脱节,都会在 migrate 那一刻暴露。最常被忽略的是——迁移文件一旦提交到 Git,就不再是“可随意编辑”的脚本,而是团队共享的事实来源。










