#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.

Reply via email to