Django多表继承迁移:带ForeignKey关联的模型转换如何防数据丢失
Django多表继承迁移:保留ForeignKey关联的实操步骤
针对你要将Person_A/Person_B改为Member子类,同时保留Group中关联关系的需求,以下是无需复杂字段复制的分步解决方案:
前置准备:备份数据库
所有操作前务必全量备份数据库,避免操作失误导致数据丢失。
步骤1:新增临时字段,留存旧ID数据
先给关联模型添加临时字段,保存当前的主键和关联ID,防止后续修改时丢失关联关系:
- 修改模型代码:
class Member(models.Model): pass class Person_A(models.Model): member_ptr = models.OneToOneField(Member, parent_link=True, on_delete=models.CASCADE) old_id = models.IntegerField(null=True) # 留存原Person_A主键 # 原有其他字段... class Person_B(models.Model): member_ptr = models.OneToOneField(Member, parent_link=True, on_delete=models.CASCADE) old_id = models.IntegerField(null=True) # 留存原Person_B主键 # 原有其他字段... class Group(models.Model): person_a = models.ForeignKey(Person_A, on_delete=models.CASCADE) person_b = models.ForeignKey(Person_B, on_delete=models.CASCADE) person_a_old_id = models.IntegerField(null=True) # 留存原关联ID person_b_old_id = models.IntegerField(null=True) # 留存原关联ID
- 生成并运行基础迁移:
python manage.py makemigrations python manage.py migrate
- 创建空迁移,用批量操作复制数据(比循环更高效):
from django.db import migrations, models def copy_old_ids(apps, schema_editor): Person_A = apps.get_model('你的应用名', 'Person_A') Person_B = apps.get_model('你的应用名', 'Person_B') Group = apps.get_model('你的应用名', 'Group') # 批量复制Person的原主键到old_id Person_A.objects.update(old_id=models.F('id')) Person_B.objects.update(old_id=models.F('id')) # 批量复制Group的原关联ID到临时字段 Group.objects.update(person_a_old_id=models.F('person_a_id')) Group.objects.update(person_b_old_id=models.F('person_b_id')) class Migration(migrations.Migration): dependencies = [ ('你的应用名', '上一步生成的迁移文件名'), ] operations = [migrations.RunPython(copy_old_ids)]
运行该迁移:
python manage.py migrate
步骤2:修改模型为多表继承,调整主键关联
将Person_A/Person_B改为继承Member,并手动调整迁移文件避免数据丢失:
- 修改模型代码:
class Member(models.Model): pass class Person_A(Member): # 暂时保留member_ptr字段,让Django识别父类关联 member_ptr = models.OneToOneField(Member, parent_link=True, on_delete=models.CASCADE, primary_key=True) old_id = models.IntegerField(null=True) # 原有其他字段... class Person_B(Member): member_ptr = models.OneToOneField(Member, parent_link=True, on_delete=models.CASCADE, primary_key=True) old_id = models.IntegerField(null=True) # 原有其他字段... class Group(models.Model): person_a = models.ForeignKey(Person_A, on_delete=models.CASCADE) person_b = models.ForeignKey(Person_B, on_delete=models.CASCADE) person_a_old_id = models.IntegerField(null=True) person_b_old_id = models.IntegerField(null=True)
- 生成迁移后手动修改迁移文件(Django自动生成的内容可能有误):
from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ ('你的应用名', '上一步生成的迁移文件名'), ] operations = [ # 将Person_A的主键切换为member_ptr(关联Member的ID) migrations.AlterField( model_name='person_a', name='member_ptr', field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='你的应用名.Member'), ), # 删除原Person_A的id字段(此时member_ptr已作为主键,数据安全) migrations.RemoveField(model_name='person_a', name='id'), # Person_B执行同样操作 migrations.AlterField( model_name='person_b', name='member_ptr', field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='你的应用名.Member'), ), migrations.RemoveField(model_name='person_b', name='id'), ]
- 运行迁移:
python manage.py migrate
步骤3:更新Group的关联关系
现在Person_A/Person_B的主键是Member的ID,需要将Group的关联从旧ID切换到新主键:
- 创建空迁移,添加关联更新逻辑:
from django.db import migrations def update_group_relations(apps, schema_editor): Group = apps.get_model('你的应用名', 'Group') Person_A = apps.get_model('你的应用名', 'Person_A') Person_B = apps.get_model('你的应用名', 'Person_B') # 批量更新Group的person_a关联 for group in Group.objects.filter(person_a_old_id__isnull=False): person_a = Person_A.objects.get(old_id=group.person_a_old_id) group.person_a_id = person_a.member_ptr_id group.save() # 批量更新Group的person_b关联 for group in Group.objects.filter(person_b_old_id__isnull=False): person_b = Person_B.objects.get(old_id=group.person_b_old_id) group.person_b_id = person_b.member_ptr_id group.save() class Migration(migrations.Migration): dependencies = [ ('你的应用名', '上一步生成的迁移文件名'), ] operations = [migrations.RunPython(update_group_relations)]
- 运行迁移:
python manage.py migrate
步骤4:清理临时字段
关联更新完成后,删除所有临时字段:
- 修改模型代码,移除所有临时字段:
class Member(models.Model): pass class Person_A(Member): # 移除member_ptr和old_id,多表继承会自动生成父类关联字段 # 原有其他字段... class Person_B(Member): # 移除member_ptr和old_id # 原有其他字段... class Group(models.Model): person_a = models.ForeignKey(Person_A, on_delete=models.CASCADE) person_b = models.ForeignKey(Person_B, on_delete=models.CASCADE) # 移除person_a_old_id和person_b_old_id
- 生成并运行迁移:
python manage.py makemigrations python manage.py migrate
内容的提问来源于stack exchange,提问作者Antony_K
相关产品推荐
相关产品推荐

