升级存在不兼容迁移的Django项目时如何优雅处理on_delete=None报错
Django2.2升级3.x外键on_delete=None问题的优雅解决方案
你原有方案最大的问题是手动修改历史迁移会破坏迁移一致性校验,多人协作或多环境部署时很容易触发迁移哈希不匹配的报错,维护成本很高,推荐使用以下两种更稳妥的方案:
方案1:加兼容补丁,无需修改历史迁移(优先推荐)
该方案完全不改动已有的历史迁移文件,通过补丁适配Django3+的校验规则,兼容性最好:
- 先停留在Django 2.2.24环境,调整模型定义,给
type外键加上on_delete=models.SET_NULL, null=True - 执行
python manage.py makemigrations生成新增字段可空属性的迁移文件,再执行python manage.py migrate将变更同步到数据库 - 在项目配置加载入口(比如项目根目录
__init__.py、settings.py顶部)添加如下兼容补丁:
import django from django.db import models from django.db.models import ForeignKey if django.VERSION >= (3, 0): original_fk_init = ForeignKey.__init__ def patched_fk_init(self, *args, **kwargs): # 自动替换历史迁移中传入的on_delete=None为合法可调用对象 if kwargs.get("on_delete") is None: # 此处可根据业务实际需要替换为CASCADE/PROTECT等其他合法值 kwargs["on_delete"] = models.SET_NULL return original_fk_init(self, *args, **kwargs) ForeignKey.__init__ = patched_fk_init
补丁会在Django版本≥3.0时自动拦截所有外键初始化逻辑,将历史迁移里的on_delete=None替换为合法值,完全避免历史迁移报错。
方案2:压缩历史迁移(适合迁移文件过多的项目)
如果不想长期保留兼容补丁,可使用Django自带的迁移压缩功能一次性解决问题:
- 先完成方案1的前两步,在Django2.2环境下生成并应用字段可空的新迁移,确保所有开发/生产环境的数据库都已同步所有现有迁移
- 执行
python manage.py squashmigrations <你的app名称> <起始迁移编号> <终止迁移编号>,Django会自动将指定范围内的历史迁移合并为一个新的迁移文件 - 打开合并后的新迁移文件,将其中所有
on_delete=None的参数替换为on_delete=models.SET_NULL - 确认所有环境都已应用完旧迁移后,删除原来的零散历史迁移文件,后续直接使用合并后的新迁移即可,不存在历史迁移校验问题。
内容的提问来源于stack exchange,提问作者GreenSmurf
相关产品推荐
相关产品推荐

