#37057: UniqueConstraint incorrectly raises ValidationError on nullable fields 
in
condition
-------------------------------------+-------------------------------------
     Reporter:  Drews                |                     Type:  Bug
       Status:  new                  |                Component:  Database
                                     |  layer (models, ORM)
      Version:  6.0                  |                 Severity:  Normal
     Keywords:  UniqueConstraint     |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
 == Summary
 When a `UniqueConstraint` has a `condition` that references a nullable
 field (e.g., `condition=Q(cash_register_type=10)` on a nullable
 `PositiveSmallIntegerField`), Django's `UniqueConstraint.validate()`
 incorrectly reports a constraint violation if the instance being saved has
 `cash_register_type=None` and another record matching the condition
 already exists.

 ''Tested in Django==5.2.4 (with Postgres 17) and 6.0.4 (with Postgres
 18)''


 == Steps to Reproduce

 {{{
 from django.db import models

 class Location(models.Model):
     name = models.CharField(max_length=100)

 class Device(models.Model):
     class CashRegisterType(models.IntegerChoices):
         MASTER = 10, "Master"
         SLAVE  = 20, "Slave"
     name = models.CharField(max_length=100)
     pos_location = models.ForeignKey(Location, on_delete=models.CASCADE)
     cash_register_type = models.PositiveSmallIntegerField(
         choices=CashRegisterType,
         blank=True, null=True, default=None,
     )

     class Meta:
         constraints = [
             models.UniqueConstraint(
                 fields=["pos_location"],
                 condition=models.Q(cash_register_type=10),
                 name="unique_master_cash_register",
                 violation_error_message="Only one Master per
 POSLocation.",
             )
         ]
 }}}

 Create one Device with `cash_register_type` Master and creating another
 one for the same Location with `cash_register_type=None` will raise
 ValidationError
 {{{
 from myapp.models import Location, Device

 loc = Location.objects.create(name="Store 1")

 # 1. Create a Master -- works
 master = Device.objects.create(
     name="CashRegister A", pos_location=loc, cash_register_type=10
 )

 # 2. Create a second Device with cash_register_type=None
 # while a Master already exists -- BUG
 register_b = Device.objects.create(
     name="Device B", pos_location=loc, cash_register_type=None
 )
 }}}

 == Workaround
 Add an explicit `isnull=False` check to the condition:
 `condition=models.Q(cash_register_type=10) &
 models.Q(cash_register_type__isnull=False)`
 Although `10` should already be `isnull=False`.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/37057>
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 visit 
https://groups.google.com/d/msgid/django-updates/0107019db4d0e696-d3c042ff-4afa-4cc3-838f-19b78656fd69-000000%40eu-central-1.amazonses.com.

Reply via email to