Trouble trying to replace the ForeignKey model

Just like the data migration example for the docs, I’ve recently realized my models setup made little sense. However, I’m having issues trying to change it.

I have a User model, a One-on-one Profile model and a Team model. Right now, Team has a FK to Profile (the field leader). I’m trying to switch it to the User model and save myself from adding select_related("leader__user")every single time :stuck_out_tongue:

My current approach

  1. Rename old FK leader_profile
  2. Create new FK leader
  3. Use a data migration to backfill information
  4. Delete leader_profile

So, in detail:

  1. Rename old FK
operations = [
  1. Create new FK:
leader_profile = models.ForeignKey(Profile)
leader = models.ForeignKey(User)
  1. Data migration
# migration file
from django.db import migrations

def backfill_leader(apps, schema_editor):
    # We can't import the Team model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Team = apps.get_model("dashboard", "Team")
    for team in Team.objects.all():
        team.leader = team.leader_profile.user

class Migration(migrations.Migration):

    operations = [
        migrations.RunPython(backfill_leader, reverse_code=migrations.RunPython.noop),
  1. Delete leader_profile

My current issue:

I’m running into a snag when trying this. The error I get is django.db.utils.ProgrammingError: relation "app_team_leader_id_bd9f9f21" already exists which is weird. I would think that renaming the field would also rename the automatic indexes. I saw there was a ticket for something related, but I’m not sure that’s what affecting me.

How do I solve this?

I think the issue here may be that you’re creating the new FK field using the same name as the old field name, and if you’re trying to run all three migrations at the same time, I can see where there might be some confusion.

I’d either try giving the new FK field a new name, running the migrations, then renaming the field back to leader, and running migrations again, or, doing as the ticket describes and adding a manual step to rename the index, or to drop the index and recreate it.

1 Like

Maybe dropping it makes more sense, since the plan is to delete the old field anyways.

I’m trying to run all this in one fell swoop as it might be easier to replicate on production. In fact, my goad was to have a single migration file too! (. ❛ ᴗ ❛.)

I’m guessing however that, since the index name was generated automatically by Django, it’s unlikely it will also be callec “app_team_leader_id_bd9f9f21” outside my local machine… is there a way to get the index name programmatically?

Postgresql provides the ‘pg_indexes’ view to show all indexes. You could query it and get all the indexes on a particular table, then find where that field appears in the definition of the index. You’d probably want to save those definitions to recreate the indexes after.

1 Like