问题
I have noticed, that I need a generalized model based on a specified model, following example should show what I mean:
before:
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)
after:
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)
Though there is already some data in the original model, so it is necessary to migrate the data to the new modelstructure
回答1:
The following answer is based on this answer(https://stackoverflow.com/a/44148102/4129587)
In order to achieve that it is necessary to do a manual Data Migration
The following 5 basic migration steps lead to the desired result:
- create the new model
TypingResult
- create a new foreign key that is nullable to the new model
TypingResult
in the old modelTextResult
- copy all the old attributes to a new instance of the new model
TypingResult
- remove the old attributes including the id from the original model
- alter the foreign key as the new primary key
It might be possible to start the migration with an autogenerated migration of the new models
The following code is based on an automatically generated migration and already tested
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'),
),
]
来源:https://stackoverflow.com/questions/44170791/django-outsource-model-properties-with-inheritance-to-a-more-general-model