Custom Validation with clean()
Right now, an animal can have two approved applications. Our rule needs a query: is another application for this animal already approved?
A rule about the whole row, not one field, goes in the model's clean() method. models.Model's clean() does nothing until we override it, and the admin calls it before it saves.
The clean() Method
Import ValidationError at the top of animals/models.py:
from datetime import date, timedelta
from django.core.exceptions import ValidationError
from django.db import models
Then add clean() to AdoptionApplication, below save():
def clean(self):
# Only an approval needs checking, and only once an animal is chosen.
if self.status != self.Status.APPROVED or self.animal_id is None:
return
others = self.animal.applications.filter(status=self.Status.APPROVED)
if others.exclude(pk=self.pk).exists():
raise ValidationError(
"Another application for this animal is already approved."
)
self.animal.applicationsis the animal's applications, through therelated_name.filter()keeps the approved ones.exclude(pk=self.pk)leaves out the application being saved.pkis short for primary key, the row'sidhere.exists()returnsTrueif any row matches.
A ValidationError raised with a message and no field name is an error about the whole application. The admin shows it with the form, and nothing is saved.
See It in the Admin
Pepper has an approved application from the last page. Open Pepper, set the other application to Approved, and click Save.
The admin shows "Please correct the error below." with our message on that row, and nothing is saved.
Two approvals saved at the same moment could both pass this check, and Django's documentation shows how a conditional UniqueConstraint makes the database refuse the second.