Reputation:
I have a two models that previously inherited from models.Model and now I've refactored them to inherit from the same base model. Django is using multi-table inheritance for this and I'm trying to generate a schema and data migration for this. There is existing data in the database which needs to be migrated.
I know that Django creates a OneToOneField, but I don't understand how it affects existing items in the database.
class BlogPost(models.Model):
name = models.CharField()
published_on = models.DateTimeField()
class AudioFile(models.Model):
file = models.FileField()
published_on = models.DateTimeField()
class Published(models.Model):
published_on = models.DateTimeField()
class BlogPost(Published):
name = models.CharField()
class AudioFile(Published):
file = models.FileField()
This was basically the migration that was generated when I ran:
./manage.py schemamigration app --auto
.
Generated file:
class Migration(SchemaMigration):
def forwards(self, orm):
db.create_table('app_published', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('published_on', self.gf('django.db.models.fields.DateTimeField')()),
))
db.send_create_signal('app', ['Published'])
db.delete_column('app_blogpost', 'published_on')
db.delete_column('app_blogpost', 'id')
db.add_column('app_blogpost', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], unique=True, primary_key=True), keep_default=False)
db.delete_column('app_audiofile', 'published_on')
db.delete_column('app_audiofile', 'id')
db.add_column('app_audiofile', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], unique=True, primary_key=True), keep_default=False)
When I try to run it, it raises an IntegrityError:
column "published_ptr_id" contains null values
Upvotes: 6
Views: 1113
Reputation: 9190
You're going to need to break that into three migrations:
Schemamigration to create the app_published table, and add your two new published_ptr columns. Add these new columns with null=True
instead of with primary_key=True
:
db.create_table('app_published', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('published_on', self.gf('django.db.models.fields.DateTimeField')()),
))
db.add_column('app_blogpost', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], null=True), keep_default=False)
db.add_column('app_audiofile', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], null=True), keep_default=False)
Datamigration to iterate over your existing audiofiles and blogposts. The code is basically:
for blogpost in orm.BlogPost.objects.all():
published = orm.Published.objects.create(published_on=blogpost.published_on)
blogpost.published_ptr = published
blogpost.save()
for audiofile in orm.AudioFile.objects.all():
published = orm.Published.objects.create(published_on=audiofile.published_on)
audiofile.published_ptr = published
audiofile.save()
Schemamigration to remove the (now unused) id and published_on columns from your old models. Also, change published_ptr from null=True
to primary_key=True
on the old models.
db.delete_column('app_blogpost', 'published_on')
db.delete_column('app_blogpost', 'id')
db.delete_column('app_audiofile', 'published_on')
db.delete_column('app_audiofile', 'id')
db.alter_column('app_blogpost', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], null=False))
db.alter_column('app_audiofile', 'published_ptr', self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['app.Published'], null=False))
db.create_index('app_blogpost', ['published_ptr'], unique=True)
db.create_index('app_audiofile', ['published_ptr'], unique=True)
db.create_primary_key('app_blogpost', ['published_ptr'])
db.create_primary_key('app_audiofile', ['published_ptr'])
Upvotes: 10