Django - отключить создание объекта Invitation, если объект User с email существует

Я хотел бы убедиться, что никто не может создать объект Invitation с email, который уже есть в базе данных либо как Invitation.email, либо как User.email.

Запретить создавать Invitation с существующими Invitation.email просто:

class Invitation(..):
    email = ...unique=True)

Возможно ли также проверить наличие email в таблице User? Я хочу сделать это на уровне базы данных или модели вместо того, чтобы проверять его в сериализаторе, формах и т.д.

Я думал о UniqueConstraint, но не знаю, как сделать так, чтобы User.objects.filter(email=email).exists() искал там.

Как насчет переопределения метода сохранения?

class Invitation(...):

    ...

    def save(self, *args, **kwargs):

        # check if an invitation email on the user table:
        if User.objects.get(id=<the-id>).email:
 
            # raise integrity error:
            ...

        # otherwise save as normal:
        else:

            super().save(*args, **kwargs)

Вы можете сделать это в модели... как показано ниже. Или вы можете сделать это в базе данных с помощью Check Constraint (предполагается, что postgres)... но вы все равно не сможете избежать добавления кода в представление, потому что вам нужно будет поймать исключение и вывести сообщение пользователю.

class Invitation(models.Model):

    def save(self, *args, **kwargs):
        if (not self.pk) and User.objects.filter(email=self.email).exists():
            raise ValueError('Cannot create invitation for existing user %s.' % self.email)
        return super().save(*args, **kwargs)

PS: Некоторые могут спросить, почему я передаю *args и **kwargs суперклассу, или возвращаю возвращаемое значение... когда save не имеет возвращаемого значения. Причина этого в том, что я никогда не предполагаю, что аргументы или возвращаемое значение метода, который я переопределяю, не изменятся в будущем. Передавать их все, если у вас нет причин перехватывать их, - это просто хорошая практика.

Вы можете переопределить метод save() на модели, и проверить сначала в таблице users. Вы должны увидеть, что это новая модель. Что-то вроде этого, я думаю:

class Invitation(..):
    email = ...unique=True)

    def save(self, *args, **kwargs):
        if self.id is None and User.objects.filter(email=self.email).exists():
            raise ValidationError('Email already used.')
        else:
            super().save(*args, **kwargs)
Вернуться на верх