Django自定义迁移未生效:调用save()未更新status字段求助
首先,你踩的这个坑其实是Django迁移系统的一个常见误区:迁移中通过apps.get_model获取的模型是历史快照版本,不是你当前models.py里带自定义save()方法的那个SOS模型。
为什么会这样?
Django迁移的核心设计是保证可重复性——每个迁移文件都会基于当时的模型结构生成快照。当你在迁移里调用apps.get_model('sos', 'SOS')时,拿到的是没有重写save()的基础Model类,所以执行sos.save()只会跑默认的保存逻辑,完全不会触发你写的状态计算代码。这就解释了为什么迁移执行极快(根本没处理业务逻辑),且所有对象的status还是默认值"New"。
快速验证这个结论
你可以在迁移的set_status函数里加一行打印,看看当前SOS模型的save方法是不是默认的:
def set_status(apps, schema_editor): SOS = apps.get_model('sos', 'SOS') print(SOS.save) # 执行迁移时会看到输出是django.db.models.Model.save,不是你自定义的方法 for sos in SOS.objects.all(): sos.save()
执行迁移后,打印结果会直接验证这个问题。
两种靠谱的解决办法
方式一:在迁移里直接实现状态计算逻辑(推荐)
既然不能依赖模型的自定义save方法,那就把save里的逻辑直接搬到迁移函数里,完全基于历史快照模型操作:
def set_status(apps, schema_editor): SOS = apps.get_model('sos', 'SOS') # 别忘了获取History模型的历史快照(替换成你的History模型实际名称) History = apps.get_model('sos', 'History') for sos in SOS.objects.all(): # 完全复刻你save方法里的逻辑 history_queryset = History.objects.filter(sos=sos) if history_queryset.exists(): latest_history = history_queryset.get(version=sos.latest_version) sos.status = latest_history.status else: sos.status = "New" # 用update_fields只更新status字段,更高效 sos.save(update_fields=['status'])
这种方式完全符合Django迁移的设计原则,不管后续模型怎么改,这个迁移都能稳定执行。
方式二:直接导入当前模型(不推荐,谨慎使用)
如果你非要用当前模型的save方法,可以直接导入,但强烈不建议——因为这会破坏迁移的可重复性,后续如果模型的save方法或字段结构变化,这个旧迁移可能直接报错:
def set_status(apps, schema_editor): from sos.models import SOS # 跳过迁移快照,直接用当前模型 for sos in SOS.objects.all(): sos.save()
这种方式虽然能快速解决当前问题,但埋下了后续迁移失败的隐患,除非你能保证这个模型的save逻辑永远不变,否则别用。
额外优化:批量处理提升速度
处理300+条数据时,循环单个save效率很低,换成bulk_update能大幅减少数据库请求:
def set_status(apps, schema_editor): SOS = apps.get_model('sos', 'SOS') History = apps.get_model('sos', 'History') update_list = [] for sos in SOS.objects.all(): history_queryset = History.objects.filter(sos=sos) if history_queryset.exists(): latest_history = history_queryset.get(version=sos.latest_version) sos.status = latest_history.status else: sos.status = "New" update_list.append(sos) # 批量更新status字段 SOS.objects.bulk_update(update_list, ['status'])
这样迁移执行速度会快很多,也更符合数据库操作的最佳实践。
内容的提问来源于stack exchange,提问作者Nealon

