I have two classes, one of which is descended from the other, and I would like to make them both sibling classes descended from the same base class.
Before:
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)
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.