# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models from django.db.models import F def seed_baseitem(apps, schema_editor): """ For each Foo, create a BaseItem with the same primary key value and the appropriate `polymorphic_ctype` value. This will be used by a new `Foo.baseitem_ptr` foreign key so that each `Foo` instance can retain its ID after `Foo` is converted to a polymorphic child class """ Foo = apps.get_model('myapp', 'Foo') BaseItem = apps.get_model('myapp', 'BaseItem') ContentType = apps.get_model('contenttypes', 'ContentType') ct = ContentType.objects.get_for_model(Foo) # if any other fields need data migrated to BaseItem, do that here BaseItem.objects.bulk_create([ BaseItem( id=obj.id, polymorphic_ctype=ct ) for obj in Foo.objects.only('id') ]) def populate_foo_baseitem_ptr(apps, schema_editor): """ Populate the `baseitem_ptr` column with the corresponding `id` value """ Foo = apps.get_model('myapp', 'Foo') Foo.objects.update(baseitem_ptr=F('id')) class Migration(migrations.Migration): """ Migration to convert Foo into a polymorphic child of BaseItem Notes: Since we are using UUID primary keys, we can reliably set them without clobbering existing records. This approach would not be sufficient for auto-incrementing integer primary keys. This migration is NOT currently reversible, because when the ordered operations of removing a primary key before adding a new one are reversed, we have the removal of the *only* remaining primary key before the restoration of the original. """ dependencies = [ ('contenttypes', '0001_initial'), ('myapp', '0001_initial'), # most recent app migration here ] operations = [ # create a BaseItem instance for each of these Items, having the same ID migrations.RunPython(seed_baseitem, migrations.RunPython.noop), # Add the `baseitem_ptr` field (but nullable, not primary key) migrations.AddField( model_name='foo', name='baseitem_ptr', field=models.OneToOneField(parent_link=True, auto_created=True, null=True, serialize=False, to='myapp.BaseItem'), preserve_default=False, ), # copy `id` (and any additional relevant values) to `baseitem_ptr` migrations.RunPython(populate_foo_baseitem_ptr, migrations.RunPython.noop), # remove the original Foo primary key migrations.RemoveField( model_name='foo', name='id', ), # make Foo.baseitem_ptr NOTNULL, and set as primary key migrations.AlterField( model_name='foo', name='baseitem_ptr', field=models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='myapp.BaseItem'), ), # run a RemoveField for any fields which are no longer needed, if data has been copied to corresponding BaseItem fields ]