Django doesn't throw a ValidationError because of a missing conditional in UniqueConstraint, and I don't know how to formulate a correct one.
One of my models contains a unique constraint including a Foreign Key:
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.
]
When submitting a new Entry which violates this constraint, the database rejects the query and Django throws an unhandled exception IntegrityError.
According to the Django documentation this is intended behaviour:
Validation of Constraints
In general constraints are not checked during full_clean(), and do not raise ValidationErrors. Rather you’ll get a database integrity error on save(). UniqueConstraints without a condition (i.e. non-partial unique constraints) are different in this regard, in that they leverage the existing validate_unique() logic, and thus enable two-stage validation. In addition to IntegrityError on save(), ValidationError is also raised during model validation when the UniqueConstraint is violated.
I would like your help with formulating a conditional, or other suggested solutions to fix this behaviour. The goal is to treat the UniqueConstraint like any other field that won't validate: Django throws a ValidationError which is caught by Django Rest Framework, and ultimately the original request will receive a HTTP 400 Bad Request, instead of 500 Internal Server Error.
Ideally I would like to implement a solution which facilitates throwing a ValidationError instead of IntegrityError from the model, instead of having to resort to the Serializer or View (The alternative solution I would like to avoid is querying the database from the View to validate the unique constraint.).
An example would be condition=Q(name=self.name, list=self.list).
However, I can't refer to the model instance from within Meta:
class Meta:
constraints = [
UniqueConstraint(fields=['list', 'name'],
condition=Q(name=self.name, list=self.list), # this is incorrect
name='unique_entry')
]
Am I trying to do something impossible? Thank you in advance, ideas and suggestions would be much appreciated.