Django: UserProfile с уникальным внешним ключом в Django Admin

Я расширил пользовательскую модель Django с помощью пользовательского профиля пользователя UserExtension. Это связано с пользователем через уникальное ForeignKey Relationship, которое позволяет мне редактировать его в администраторе в встроенной форме! Я использую сигнал для создания нового профиля для каждого нового пользователя:

def create_user_profile(sender, instance, created, **kwargs):  
    if created:
        try:  
            profile, created = UserExtension.objects.get_or_create(user=instance)
        except:
            pass  

post_save.connect(create_user_profile, sender=User) 

(как описано здесь, например: Расширение модели User с настраиваемыми полями в Django) Проблема в том, что, если я создаю нового пользователя через администратора, я получаю IntegritiyError при сохранении "column user_id не уникален". Кажется, что сигнал вызывается дважды, но я думаю, что администратор пытается сохранить профиль AFTERWARDS? Но мне нужно создание через сигнал, если я создаю нового пользователя в других частях системы!

Ответ 1

Это нормально, что django впоследствии создаст экземпляр администратора, поскольку сохранение всегда состоит из следующего:

  • Создать объект пользователя
  • Создать объект профиля (не может быть раньше, поскольку он указывает на пользователя).

При сохранении объекта User django ORM не может знать, что объект создания профиля появится после него, чтобы он не задерживал сигнал post_save каким-либо образом (даже не имеет смысла).

Лучший способ справиться с этим (imho), если вы хотите сохранить сигнал post_save, - это переопределить метод сохранения UserExtension примерно так:

def save(self, *args, **kwargs):
    try:
        existing = UserExtension.objects.get(user=self.user)
        self.id = existing.id #force update instead of insert
    except UserExtension.DoesNotExist:
        pass 
    models.Model.save(self, *args, **kwargs)

Обратите внимание, что это заставляет каждую вставку указывать одному и тому же пользователю, что и существующий объект, чтобы стать обновлением, это может быть неожиданное поведение для других частей кода.