Ответ 1
Проблему можно избежать, установив primary_key=True
на OneToOneField
, указывая на модель User
, как вы сами поняли.
Причина, по которой это работает, кажется довольно простой.
При попытке создать экземпляр модели и установить pk
вручную перед ее сохранением, Django попытается найти запись в базе данных с этим pk
и обновить ее, а не слепо пытаться создать новую. Если он не существует, он создает новую запись, как ожидалось.
Когда вы устанавливаете OneToOneField
в качестве первичного ключа, а Django Admin устанавливает это поле в соответствующий идентификатор модели User
, это означает, что pk
уже установлен, и Django попытается сначала найти существующую запись.
Это то, что происходит с OneToOneField
в качестве первичного ключа:
- Django Admin создает новый экземпляр
User
безid
. - Django Admin сохраняет экземпляр
User
.- Так как
pk
(в данном случаеid
) не установлен, Django пытается создать новую запись. - Новая запись
id
автоматически устанавливается базой данных. - Крючок
post_save
создает новый экземплярProfile
для этого экземпляраUser
.
- Так как
- Django Admin создает новый экземпляр
Profile
с егоUser
, установленным пользователемid
. - Django Admin сохраняет экземпляр
Profile
.- Так как
pk
(в данном случаеUser
) уже установлен, Django пытается извлечь существующую запись с этимpk
. - Django находит существующую запись и обновляет ее.
- Так как
Если ядро не задает первичный ключ, Django вместо этого добавляет поле, использующее функциональность базы данных auto_increment
: база данных устанавливает pk
в следующее наибольшее значение, которое не существует. Это означает, что поле фактически останется пустым, если вы не установите его вручную, и поэтому Django всегда будет пытаться вставить новую запись, что приведет к конфликту с ограничением уникальности на OneToOneField
.
Вот что вызывает исходную проблему:
- Django Admin создает новый экземпляр
User
безid
. - Django Admin сохраняет экземпляр
User
, крюкpost_save
, создавая новый экземплярProfile
, как и раньше. - Django Admin создает новый экземпляр
Profile
безid
(автоматически добавленное полеpk
). - Django Admin сохраняет экземпляр
Profile
.- Так как
pk
(в данном случаеid
) не установлен, Django пытается создать новую запись. - В базе данных сообщается о нарушении ограничений уникальности таблицы в поле
User
. - Django выбрасывает исключение. Сегодня вы не пойдете в космос.
- Так как