Django:将继承的模型属性外包给更通用的模型
Django: outsource model properties with inheritance to a more general model
我注意到,我需要一个基于指定模型的通用模型,下面的例子应该能说明我的意思:
之前:
class TextResult(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
text = models.ForeignKey(Text)
wpm = models.FloatField(default=0.0)
accuracy = models.FloatField(default=1.0)
之后:
class TypingResult(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
wpm = models.FloatField(default=0.0)
accuracy = models.FloatField(default=1.0)
class TextResult(TypingResult):
text = models.ForeignKey(Text)
虽然原来的模型中已经有一些数据,所以需要将数据迁移到新的模型结构中
以下答案基于此答案()
为了实现,有必要进行手动数据迁移
以下 5 个基本迁移步骤可实现预期结果:
- 创建新模型
TypingResult
- 在旧模型
TextResult
中创建新模型 TypingResult
可以为空的新外键
- 将所有旧属性复制到新模型的新实例中
TypingResult
- 从原始模型中删除包括 id 在内的旧属性
- 将外键更改为新的主键
可能可以通过自动生成的新模型迁移来开始迁移
以下代码基于自动生成的迁移并已经过测试
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
def copy_text_results_to_typing_results(apps, schema_editor):
TypingResult = apps.get_model('testapp', 'TypingResult')
TextResult = apps.get_model('testapp', 'TextResult')
for text_result in TextResult.objects.all():
copied_result = TypingResult()
copied_result.user = text_result.user
copied_result.wpm = text_result.wpm
copied_result.accuracy = text_result.accuracy
copied_result.save()
text_result.typingresult_ptr = copied_result
text_result.save()
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('testapp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='TypingResult',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')),
('wpm', models.FloatField(default=0.0)),
('accuracy', models.FloatField(default=1.0)),
('user', models.ForeignKey(default=1,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL)),
],
),
# add the foreign key for the new inherited model,
# it is allowed to have null values since the actual values have to be
# copied first to this, it will be changed later
migrations.AddField(
model_name='textresult',
name='typingresult_ptr',
field=models.OneToOneField(blank=True, null=True, to='testapp.TypingResult'),
),
# copy the old values to the new inherited model
migrations.RunPython(copy_text_results_to_typing_results),
# remove the old id and the copied fields from the TextResult model
migrations.RemoveField(
model_name='textresult',
name='accuracy',
),
migrations.RemoveField(
model_name='textresult',
name='id',
),
migrations.RemoveField(
model_name='textresult',
name='user',
),
migrations.RemoveField(
model_name='textresult',
name='wpm',
),
# alter the id of the inherited model to be the new primary key
migrations.AlterField(
model_name='textresult',
name='typingresult_ptr',
field=models.OneToOneField(auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to='testapp.TypingResult'),
),
]
我注意到,我需要一个基于指定模型的通用模型,下面的例子应该能说明我的意思:
之前:
class TextResult(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
text = models.ForeignKey(Text)
wpm = models.FloatField(default=0.0)
accuracy = models.FloatField(default=1.0)
之后:
class TypingResult(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
wpm = models.FloatField(default=0.0)
accuracy = models.FloatField(default=1.0)
class TextResult(TypingResult):
text = models.ForeignKey(Text)
虽然原来的模型中已经有一些数据,所以需要将数据迁移到新的模型结构中
以下答案基于此答案(
为了实现,有必要进行手动数据迁移
以下 5 个基本迁移步骤可实现预期结果:
- 创建新模型
TypingResult
- 在旧模型
TextResult
中创建新模型 - 将所有旧属性复制到新模型的新实例中
TypingResult
- 从原始模型中删除包括 id 在内的旧属性
- 将外键更改为新的主键
TypingResult
可以为空的新外键
可能可以通过自动生成的新模型迁移来开始迁移
以下代码基于自动生成的迁移并已经过测试
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
def copy_text_results_to_typing_results(apps, schema_editor):
TypingResult = apps.get_model('testapp', 'TypingResult')
TextResult = apps.get_model('testapp', 'TextResult')
for text_result in TextResult.objects.all():
copied_result = TypingResult()
copied_result.user = text_result.user
copied_result.wpm = text_result.wpm
copied_result.accuracy = text_result.accuracy
copied_result.save()
text_result.typingresult_ptr = copied_result
text_result.save()
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('testapp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='TypingResult',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')),
('wpm', models.FloatField(default=0.0)),
('accuracy', models.FloatField(default=1.0)),
('user', models.ForeignKey(default=1,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL)),
],
),
# add the foreign key for the new inherited model,
# it is allowed to have null values since the actual values have to be
# copied first to this, it will be changed later
migrations.AddField(
model_name='textresult',
name='typingresult_ptr',
field=models.OneToOneField(blank=True, null=True, to='testapp.TypingResult'),
),
# copy the old values to the new inherited model
migrations.RunPython(copy_text_results_to_typing_results),
# remove the old id and the copied fields from the TextResult model
migrations.RemoveField(
model_name='textresult',
name='accuracy',
),
migrations.RemoveField(
model_name='textresult',
name='id',
),
migrations.RemoveField(
model_name='textresult',
name='user',
),
migrations.RemoveField(
model_name='textresult',
name='wpm',
),
# alter the id of the inherited model to be the new primary key
migrations.AlterField(
model_name='textresult',
name='typingresult_ptr',
field=models.OneToOneField(auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to='testapp.TypingResult'),
),
]