Сформулируйте условный Q на уникальном ограничении для обработки исключений

Django не бросает ValidationError из-за отсутствующего условия в UniqueConstraint, и я не знаю, как правильно его сформулировать.


Одна из моих моделей содержит уникальное ограничение, включающее внешний ключ:

class Entry(models.Model):
    """
    Entry on a List
    """
    name= models.CharField()
    expiration_date = models.DateField()
    list = models.ForeignKey(List,
                             related_name="entries",
                             on_delete=models.CASCADE)
    ...

    class Meta:
        constraints = [
            UniqueConstraint(fields=['list', 'name'],
                             name='unique_entry')  # Each entry_name may only occur once per list.
        ]

При отправке новой записи, нарушающей это ограничение, база данных отклоняет запрос и Django выбрасывает необработанное исключение IntegrityError.

Согласно документации по Django предполагается следующее поведение:

Валидация ограничений

В общем случае ограничения не проверяются во время full_clean(), и не вызывают ошибки ValidationError. Скорее вы получите ошибку целостности базы данных на save(). UniqueConstraints без condition (т.е. нечастичные уникальные ограничения) отличаются в этом отношении тем, что они используют существующую логику validate_unique(), и таким образом обеспечивают возможность двухэтапную валидацию. В дополнение к IntegrityError на save(), ValidationError также возникает во время проверки модели, когда UniqueConstraint нарушается.

Мне нужна ваша помощь в формулировке условия или других предложенных решений для исправления этого поведения. Цель состоит в том, чтобы относиться к UniqueConstraint как к любому другому полю, которое не проходит валидацию: Django бросает ValidationError, который перехватывается Django Rest Framework, и в конечном итоге исходный запрос получит HTTP 400 Bad Request, вместо 500 Internal Server Error.

В идеале я хотел бы реализовать решение, которое позволяет выбрасывать ValidationError вместо IntegrityError из модели, вместо того, чтобы прибегать к Serializer или View (Альтернативное решение, которого я хотел бы избежать, это запрос к базе данных из View для проверки уникального ограничения).

Примером может служить condition=Q(name=self.name, list=self.list). Однако я не могу обратиться к экземпляру модели из Meta:

class Meta:
        constraints = [
            UniqueConstraint(fields=['list', 'name'],
                             condition=Q(name=self.name, list=self.list), # this is incorrect
                             name='unique_entry')
        ]

Я пытаюсь сделать что-то невозможное? Заранее спасибо, идеи и предложения будут очень признательны.

Вы можете добавить проверку в метод clean вашей модели. Если вы затем используете ModelForm, он вызовет метод clean() и таким образом проверит, является ли элемент действительным.

Таким образом, модель выглядит следующим образом:

from django.core.exceptions import ValidationError

class Entry(models.Model):
    name= models.CharField()
    expiration_date = models.DateField()
    list = models.ForeignKey(
        List,
        related_name='entries',
        on_delete=models.CASCADE
    )
    
    def clean(self, *args, **kwargs):
        qs = Entry.objects.exclude(pk=self.pk).filter(name=self.name, list_id=self.list_id)
        if qs.exists():
            raise ValidationError('Can not add an entry with the same name to a list')
        return super().clean(*args, **kwargs)
    
    class Meta:
        constraints = [
            UniqueConstraint(
                fields=['list', 'name'],
                name='unique_entry'
            )
        ]
Вернуться на верх