You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django将抽象Symbol模型改为非抽象并调整外键的迁移问题

解决Django抽象模型改非抽象+外键迁移的数据保留问题

我之前也遇到过一模一样的场景,把抽象模型改成非抽象时,Django的多表继承机制会自动给子类添加symbol_ptr外键字段,但现有数据没有对应的Symbol记录,所以才会弹出这个缺少默认值的提示。下面是一步步不丢失数据的解决方法:

步骤1:处理Symbol从抽象到非抽象的迁移,迁移现有DetailSymbol1的field1数据到Symbol表

1.1 临时修改模型,允许symbol_ptr为空

首先我们先让Django能顺利生成迁移,暂时手动给DetailSymbol1添加允许为空的symbol_ptr字段:

class Symbol(BaseModel):
    field1 = models.CharField(max_length=50)

class DetailSymbol1(Symbol):
    field2 = models.CharField(max_length=50)
    # 手动添加这个字段,暂时允许为空,后续会去掉
    symbol_ptr = models.ForeignKey(Symbol, null=True, on_delete=models.CASCADE, editable=False)

class DailyData(BaseModel):
    symbol = models.ForeignKey(DetailSymbol1, on_delete=models.CASCADE)
    field3 = models.CharField(max_length=50)

1.2 生成并执行初始迁移

运行以下命令生成迁移,这时候不会再提示缺少默认值:

python manage.py makemigrations
python manage.py migrate

1.3 创建数据迁移,把DetailSymbol1的field1迁移到Symbol表

生成一个空的迁移文件:

python manage.py makemigrations --empty your_app_name

打开生成的空迁移文件,添加数据迁移函数,把每个DetailSymbol1的field1提取出来创建对应的Symbol记录,并关联symbol_ptr:

from django.db import migrations

def migrate_detail_to_symbol(apps, schema_editor):
    Symbol = apps.get_model('your_app_name', 'Symbol')
    DetailSymbol1 = apps.get_model('your_app_name', 'DetailSymbol1')
    
    # 遍历所有DetailSymbol1,创建对应的Symbol并关联
    for detail in DetailSymbol1.objects.all():
        symbol = Symbol.objects.create(field1=detail.field1)
        detail.symbol_ptr = symbol
        detail.save()

class Migration(migrations.Migration):
    dependencies = [
        # 这里替换成上一步生成的迁移文件名,比如'your_app_name.0002_auto_xxxxxx'
        ('your_app_name', '0002_auto_20240520_1234'),
    ]

    operations = [
        migrations.RunPython(migrate_detail_to_symbol),
    ]

执行这个数据迁移:

python manage.py migrate

1.4 把symbol_ptr设为非空,恢复多表继承的默认结构

现在所有DetailSymbol1都有对应的Symbol记录了,我们可以去掉手动添加的symbol_ptr字段,回到原始模型:

class Symbol(BaseModel):
    field1 = models.CharField(max_length=50)

class DetailSymbol1(Symbol):
    field2 = models.CharField(max_length=50)

class DailyData(BaseModel):
    symbol = models.ForeignKey(DetailSymbol1, on_delete=models.CASCADE)
    field3 = models.CharField(max_length=50)

生成并执行迁移,这时候Django会自动把symbol_ptr字段设为非空:

python manage.py makemigrations
python manage.py migrate

步骤2:修改DailyData的外键为Symbol,迁移现有数据

2.1 修改DailyData的模型外键

更新模型,把DailyData的外键从DetailSymbol1改为Symbol:

class DailyData(BaseModel):
    symbol = models.ForeignKey(Symbol, on_delete=models.CASCADE)
    field3 = models.CharField(max_length=50)

2.2 生成并编辑迁移文件

运行makemigrations,当提示是否提供默认值时,选择2) Quit:

python manage.py makemigrations

打开生成的迁移文件,添加数据迁移函数,把现有DailyData的symbol字段从DetailSymbol1关联到对应的Symbol记录:

from django.db import migrations

def update_dailydata_symbol(apps, schema_editor):
    DailyData = apps.get_model('your_app_name', 'DailyData')
    DetailSymbol1 = apps.get_model('your_app_name', 'DetailSymbol1')
    
    for daily in DailyData.objects.all():
        # 关联到DetailSymbol1对应的Symbol
        daily.symbol = daily.symbol.symbol_ptr
        daily.save()

class Migration(migrations.Migration):
    dependencies = [
        # 替换成上一步的迁移文件名
        ('your_app_name', '0004_auto_20240520_1235'),
    ]

    operations = [
        # 先执行数据更新,再修改字段类型
        migrations.RunPython(update_dailydata_symbol),
        migrations.AlterField(
            model_name='dailydata',
            name='symbol',
            field=models.ForeignKey(on_delete=models.db.models.deletion.CASCADE, to='your_app_name.Symbol'),
        ),
    ]

2.3 执行最终迁移

运行迁移完成所有修改:

python manage.py migrate

验证数据

现在可以进入Django shell验证数据是否完整:

python manage.py shell
from your_app_name.models import DetailSymbol1, DailyData, Symbol

# 查看Symbol数量,应该和原来的DetailSymbol1数量一致
print(Symbol.objects.count()) # 应该是1
# 查看DailyData数量,还是2
print(DailyData.objects.count()) # 应该是2
# 验证关联关系
a = DetailSymbol1.objects.first()
print(a.symbol_ptr.field1) # 输出"a"
print(a.dailydata_set.all()[0].symbol.field1) # 输出"a"

这样就完成了所有模型修改,同时保留了所有原有数据。

内容的提问来源于stack exchange,提问作者user3595632

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:31:58