[Fixed]-Creating a profile model with both an InlineAdmin and a post_save signal in Django

25👍

The problem can be avoided by setting primary_key=True on the OneToOneField pointing at the User model, as you have figured out yourself.

The reason that this works seems to be rather simple.

When you try to create a model instance and set the pk manually before saving it, Django will try to find a record in the database with that pk and update it rather than blindly attempting to create a new one. If none exists, it creates the new record as expected.

When you set the OneToOneField as the primary key and Django Admin sets that field to the related User model’s ID, that means the pk is already set and Django will attempt to find an existing record first.

This is what happens with the OneToOneField set as primary key:

  1. Django Admin creates the new User instance, with no id.
  2. Django Admin saves the User instance.
    1. Because the pk (in this case id) is not set, Django attempts to create a new record.
    2. The new record’s id is set automatically by the database.
    3. The post_save hook creates a new Profile instance for that User instance.
  3. Django Admin creates the new Profile instance, with its user set to the user’s id.
  4. Django Admin saves the Profile instance.
    1. Because the pk (in this case user) is already set, Django attempts to fetch an existing record with that pk.
    2. Django finds the existing record and updates it.

If you don’t set the primary key explicitly, Django instead adds a field that uses the database’s auto_increment functionality: the database sets the pk to the next largest value that doesn’t exist. This means the field will actually be left blank unless you set it manually and Django will therefore always attempt to insert a new record, resulting in a conflict with the uniqueness-constraint on the OneToOneField.

This is what causes the original problem:

  1. Django Admin creates the new User instance, with no id.
  2. Django Admin saves the User instance, the post_save hook creating a new Profile instance as before.
  3. Django Admin creates the new Profile instance, with no id (the automatically added pk field).
  4. Django Admin saves the Profile instance.
    1. Because the pk (in this case id) is not set, Django attempts to create a new record.
    2. The database reports a violation of the table’s uniqueness-constraint on the user field.
    3. Django throws an Exception. You will not go to space today.

3👍

It seems like setting primary_key=True on the OneToOneField connecting the profile model to the User model fixes this issue. However, I don’t think I understand all the implications of that and why it helps.

I’ll leave this here as a hint, but if that’s the best solution and someone could come up with a well-written explanation, I’d upvote/accept that and possibly delete mine.

Leave a comment