2

I have a model LucyGuide which extends Django's User model through a OneToOneField:

class LucyGuide(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

There is also a Company model which has a field called lucy_guide which is a ForeignKey to the User model:

class Company(models.Model):
    lucy_guide = models.ForeignKey(User)

I'd like to change this to

class Company(models.Model):
    lucy_guide = models.ForeignKey(LucyGuide)

However, when I implement this change and make and run migrations, I run into an IntegrityError:

django.db.utils.IntegrityError: insert or update on table "lucy_web_company" violates foreign key constraint "lucy_web_company_lucy_guide_id_de643702_fk_lucy_web_"
DETAIL:  Key (lucy_guide_id)=(461) is not present in table "lucy_web_lucyguide".

This question is similar to IntegrityError Insert or update on table "orders_order" violates foreign key constraint "; it seems like I have created LucyGuide objects before referencing them as foreign keys.

What is the best way to fix this? Is there a series of commands I need to write in the shell to create these users?

Update

From looking around in the shell, it seems like Django still expects the same numerical ids for the ForeignKey, even though the model has changed (from User to LucyGuide). Here are the ids of Users which are also LucyGuides:

In [11]: lucy_guide_users = [user for user in User.objects.all() if hasattr(user, 'lucyguide')]

In [16]: [user.id for user in lucy_guide_users]
Out[16]: [12, 8, 461, 497, 500, 471, 475, 495]

Notice that this contains the id of 461 from the error. The ids of LucyGuides, however, are simply

In [17]: [guide.id for guide in LucyGuide.objects.all()]
Out[17]: [1, 2, 3, 4, 5, 6, 7, 8]

It seems like the way to fix this is to change the primary keys of the LucyGuides, but it seems from What is the best approach to change primary keys in an existing Django app? that this is a very involved process. Is there a simpler way?

Kurt Peek
  • 52,165
  • 91
  • 301
  • 526
  • You shouldn't try to change the primary keys of the objects in the `LucyGuide` table. Instead you want to change the `lucy_guide_id` values in the `Company` table so that they reference `LucyGuide` rows. Unfortunately, that's quite an involved process. The [question you linked](https://stackoverflow.com/questions/2055784/what-is-the-best-approach-to-change-primary-keys-in-an-existing-django-app) to is about changing the field type of a primary key (e.g. from a `CharField` to `AutoField`), which is a different problem. – Alasdair Mar 30 '18 at 20:53

1 Answers1

6

You can't simply change the target of the foreign key. For each existing company, you need to change lucy_guide_id from the related user's id to the related lucy_guide's id. Django can't do this for you.

You could do the migration in several steps. First, add a new foreign key and create a migration

class Company(models.Model):
    lucy_guide = models.ForeignKey(User)
    new_lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)

Next, create a data migration to populate the new_lucy_guide field.

Then you can drop the lucy_guide field, and create a migration.

class Company(models.Model):
    new_lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)

Finally you can rename your field and create a migration:

class Company(models.Model):
    lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • 1
    Just as a precision: you can't do the last two steps at once. It will see the new field as `RemoveField` and consider the name change as `AlterField` on the old one. – Pierre Monico Jun 17 '19 at 09:53
  • @PierreMonico thanks for confirming. I've removed the paragraph that suggests you might be combine the last two steps in one migration. – Alasdair Jun 17 '19 at 13:35
  • The only way to do it would be to edit the migration file manually. – Pierre Monico Jun 18 '19 at 11:48