﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
33996	Inconsistent behavior of CheckConstraints validation on None values.	James Beith	David Sanders	"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.
"	Bug	closed	Database layer (models, ORM)	4.1	Release blocker	fixed		Gagaro David Sanders Simon Charette	Ready for checkin	1	0	0	0	0	0
