Django执行makemigrations重复生成同一迁移文件的问题求助
问题现象
- 执行
migrate命令时,反复收到模型未同步提示:
Your models in app(s): 'organization_app' have changes that are not yet reflected in a migration, and so won't be applied.
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
- 执行
makemigrations会重复生成针对PenaltyModel.tenant_penalty_choice字段的迁移文件,即便已完成migrate操作 - 用
sqlmigrate查看该迁移,输出为无实际操作的SQL:
BEGIN; -- -- Alter field tenant_penalty_choice on penaltymodel -- -- (no-op) COMMIT;
- 删除所有迁移文件重建后,问题依旧存在
问题根源
这不是Django的Bug,核心原因是Django迁移系统无法识别自定义枚举类生成的choices与迁移文件中硬编码选项的一致性。
你的模型中使用了TenantPenaltyChoices.choices动态引用枚举类选项,但生成的迁移文件里直接写死了choices=[("PERCENT", "Percent"), ("AMOUNT", "Amount")]静态列表。当Django对比模型定义与迁移记录时,会判定两者的choices来源不同,因此认为模型存在未同步的修改。
解决步骤
方法1:修改迁移文件使用枚举类引用(推荐)
编辑生成的迁移文件,将硬编码的choices替换为枚举类的直接引用:
# Generated by Django 4.2.3 on 2024-02-07 20:14 from django.db import migrations, models # 导入你的枚举类 from organization_app.models import TenantPenaltyChoices class Migration(migrations.Migration): dependencies = [ ("organization_app", "0002_alter_penaltymodel_tenant_penalty_choice"), ] operations = [ migrations.AlterField( model_name="penaltymodel", name="tenant_penalty_choice", field=models.CharField( choices=TenantPenaltyChoices.choices, default=TenantPenaltyChoices.AMOUNT, max_length=20, ), ), ]
执行migrate后,Django会判定模型与迁移的choices来源一致,不会再重复生成迁移。
方法2:模型中直接硬编码choices(不推荐)
若不想修改迁移文件,可将模型里的choices改为硬编码列表:
class PenaltyModel(models.Model): tenant_penalty_desc = models.TextField(null=False) tenant_penalty_name = models.CharField(max_length=100, null=False) tenant_penalty_percentage = models.IntegerField(default=0) tenant_penalty_amount = models.FloatField(default=0) tenant_penalty_choice = models.CharField( choices=[("PERCENT", "Percent"), ("AMOUNT", "Amount")], max_length=20, default="AMOUNT", ) rental_house = models.OneToOneField(HouseModel, on_delete=models.CASCADE)
这种方式虽能解决问题,但会丢失枚举类的可维护性,仅适用于选项固定不变的场景。
方法3:彻底重置迁移状态(极端场景)
若前两种方法无效,可尝试重置迁移:
- 备份数据库(重要操作)
- 删除
organization_app/migrations/目录下除__init__.py外的所有文件 - 删除数据库
django_migrations表中所有属于organization_app的记录 - 执行
python manage.py makemigrations organization_app生成全新初始迁移 - 执行
python manage.py migrate --fake-initial organization_app标记初始迁移为已应用
注意:--fake-initial仅适用于数据库表已与当前模型结构一致的情况,使用前需确认结构匹配。
内容的提问来源于stack exchange,提问作者slinger

