What value do I use for _ptr when migrating reparented classes with South?

You do this in separate phases.

Phase 1: Create your "Base" model in the code. On the A and B models, add base_ptr as a nullable FK to Base (the name base_ptr is made by lowercasing the class-name Base, adapt your names accordingly). Specify db_column='base_ptr' on the new column, so you don't get an _id suffix added. Don't change parenthood yet: Keep B as a child of A and A as it was before (Base has no child classes yet). Add a migration to make the respective database changes, and run it.

Phase 2: Create a data migration, copying relevant data around. You should probably copy all A data into Base, remove redundant A records (those that served B instances), and in remaining records (of both A and B) copy the id into base_ptr. Note that the child class B uses two tables -- its id field comes from A's table, and on its own table there is a field a_ptr which is a FK to A -- so your update operation will be more efficient if you copy values from a_ptr to base_ptr. Make sure the copying into base_ptr occurs after the copying into the Base table, so you don't violate the FK constraints.

Phase 3: Now change the models again -- remove the explicit base_ptr FK and change parents to the way you like, and create a third migration (automatic schema migration). Note that setting the parent to Base implicitly defines a non-nullable base_ptr field, so with respect to the base_ptr fields, you are only changing a nullable field into non-nullable, and no default is needed.

You should still be asked for a default value for a_ptr -- the implicit FK from B to A that is removed when the parent is changed from A to Base; the default is needed for the migration in the backward direction. You can either do something that will fail the backward migration, or, if you do want to support it, add an explicit nullable a_ptr to B, like the base_ptr columns you used before. This nullable column can then be removed in a fourth migration.


If base will not be instantiated on its own, you can easily solve the problem using abstract = True prop to class Meta.

Example code:

from django.db import models

class Base(models.Model):
    name = models.CharField(max_length=10)
    class Meta:
        abstract = True

class A(Base):
    pass

class B(Base):
    title = models.CharField(max_length=10)

Tags:

Django South