#33996: Inconsistent behaviour validating constraints between Django and the
database
-------------------------------------+-------------------------------------
Reporter: James | Owner: nobody
Beith |
Type: Bug | Status: new
Component: Database | Version: 4.1
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
It appears that Django and the database validate check constraints
differently.
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.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/010701831b5cb944-9e2b211b-e5e6-4554-8781-1cb84742c5d8-000000%40eu-central-1.amazonses.com.