Skip to content

Instantly share code, notes, and snippets.

@smcoll
Last active March 24, 2017 22:29
Show Gist options
  • Save smcoll/5dc21fd7c35e06f0c34180ce295ca8ce to your computer and use it in GitHub Desktop.
Save smcoll/5dc21fd7c35e06f0c34180ce295ca8ce to your computer and use it in GitHub Desktop.
Django migration for converting model to django-polymorphic child model
# -*- 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
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment