Migrations: Turning a PK field into a FK + PK field

Dear Django community,

in a project, I’m struggling to fix a problem with my database schema and data migrations.

Here’s what I want to do:

My application used to have a “Person” model. As time went by, it turned out that we would also need an model for “Organization”. Both should inherit the (non-abstract) model “Actor”, that holds attributes that are common between Organizaztions and Persons.

That’s what my models should look like in the end:

class Actor(models.Model):
    # Actor is new!
    name = models.CharField(...)

class Organization(Actor):
     # organization is new, too!
     # organization attributes ...

class Person(Actor):
    biography = models.TextField(...)

To keep existing data, I created an Actor-instance for each person (using the person’s PK value) and used a data migration to copy the attribute values that are common for both Organiation and Person to Actor.

Next, I turned the Person model’s PK field into a PK + FK field, thereby linking it with the Actor table:

class Migration(migrations.Migration):
    dependencies = [
        ('resources', 'xxx'),
    ]

    operations = [
        migrations.RenameField(
             model_name='person',
             old_name='id',
             new_name='actor_ptr_id',
        )
        migrations.AlterField(
            model_name='person',
            name='actor_ptr_id',
            field=models.OneToOneField(auto_created=True, default=0, on_delete=django.db.models.deletion.CASCADE,
                                       parent_link=True, primary_key=True, serialize=False, to='resources.Actor',
                                       db_column='actor_ptr_id'),
            preserve_default=False
        ),
    ]

My Django app does everything it should do now – data from both tables (persons and actor) are joined when loading person instances from the database, just as it should.

Here’s the hard part:

Whenever I run ./manage.py makemigrations now, a new migration is created. The migration first drops my actor_ptr_id field and then re-creates it (see migration code below). When applying this migration, my relations between person and actor model would break. The point seems to be that Django doesn’t accept creating actor_ptr_id links manually. Any ideas how to prevent Django from re-creating this migration over and over again?

class Migration(migrations.Migration):

    dependencies = [
        ('resources', 'xxx'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='person',
            name='actor_ptr_id',
        ),
        migrations.AddField(
            model_name='person',
            name='actor_ptr',
            field=models.OneToOneField(auto_created=True, default=0, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='resources.actor'),
            preserve_default=False,
        ),
    ]

Thanks for any ideas and help!

Best regards
Julian

Do you have a field defined in your model named actor_ptr_id with primary_key=True?

1 Like

Thanks @KenWhitesell – Declaring the field explicitly fixes the issue prevents Django from creating the migration. Thanks!

class Person(Actor):
    actor_ptr_id = models.OneToOneField(Actor,
        on_delete=models.CASCADE,
        parent_link=True,
        primary_key=True,
        db_column='actor_ptr_id')