Let's say we have this model.
{{{
class Person(models.Model):
age = models.PositiveIntegerField(blank=True, null=True)
class Meta:
constraints = [
models.CheckConstraint(check=Q(age__gte=18), name="age_gte_18")
]
}}}
If we try to create a person in the database it will raise an error if we
violate the constraint.
{{{
>>> Person.objects.create(age=15)
IntegrityError: new row for relation "data_person" violates check
constraint "age_gte_18"
DETAIL: Failing row contains (1, 15).
}}}
And if the age is `None` then the database will not check the constraint,
so the object is saved to the database.
{{{
>>> Person.objects.create(age=None)
<Person: Person object (2)>
}}}
In Django 4.1 we have this new behaviour
> Check, unique, and exclusion constraints defined in the Meta.constraints
option are now checked during model validation.
So if we do this, we do still get the same behaviour as the database.
{{{
>>> person = Person(age=15)
>>> person.full_clean()
ValidationError: {'__all__': ['Constraint "age_gte_18" is violated.']}
}}}
But if the age is `None` we now get the validation error, even though the
database will happily save the object.
{{{
>>> person = Person(age=None)
>>> person.full_clean()
ValidationError: {'__all__': ['Constraint "age_gte_18" is violated.']}
>>> person.save() # successfully saves to the database
}}}
Is this the expected behaviour?
Should Django's validation be taking into account if the field's define
with `null=True` and not raise the validation error (so same behaviour as
the database)?
Alternatively, is it that the constraint needs updating to also support
null so the Django validation passes? e.g.
{{{
models.CheckConstraint(check=Q(age__isnull=True) | Q(age__gte=18),
name="age_gte_18")
}}}
Note: Tested with PostgreSQL, not sure if different databases treat nulls
as not breaking the constraint differently.
--
Ticket URL: <https://code.djangoproject.com/ticket/33996>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.