#34871: Validation fails for models with UniqueConstraints that include Case
statements
-------------------------------------+-------------------------------------
               Reporter:  Andrew     |          Owner:  nobody
  Roberts                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  4.2
  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          |
-------------------------------------+-------------------------------------
 Consider the following model where we want to guarantee that there is only
 one active profile per email address:

 {{{
 class Profile(models.Model):
     active = models.BooleanField()
     email = models.CharField(max_length=255)

     class Meta:
         constraints = [
             models.UniqueConstraint(
                 Case(When(active=True, then=F("email"))),
                 name="unique_active_email",
             )
         ]
         app_label = "test"
 }}}

 Model validation indirectly calls constraint.validate(), but to simulate
 this we can execute:

 {{{
 constraint = Profile._meta.constraints[0]
 constraint.validate(Profile, Profile(active=True,
 email="[email protected]"))
 }}}

 This fails with:
 {{{
 ---------------------------------------------------------------------------
 AttributeError                            Traceback (most recent call
 last)
 Cell In[21], line 1
 ----> 1 constraint.validate(Profile, Profile(active=True,
 email="[email protected]"))

 File /usr/local/lib/python3.11/site-
 packages/django/db/models/constraints.py:346, in
 UniqueConstraint.validate(self, model, instance, exclude, using)
     344         if isinstance(expr, OrderBy):
     345             expr = expr.expression
 --> 346         expressions.append(Exact(expr,
 expr.replace_expressions(replacements)))
     347     queryset = queryset.filter(*expressions)
     348 model_class_pk = instance._get_pk_val(model._meta)

 File /usr/local/lib/python3.11/site-
 packages/django/db/models/expressions.py:401, in
 BaseExpression.replace_expressions(self, replacements)
     398 clone = self.copy()
     399 source_expressions = clone.get_source_expressions()
     400 clone.set_source_expressions(
 --> 401     [
     402         expr.replace_expressions(replacements) if expr else None
     403         for expr in source_expressions
     404     ]
     405 )
     406 return clone

 File /usr/local/lib/python3.11/site-
 packages/django/db/models/expressions.py:402, in <listcomp>(.0)
     398 clone = self.copy()
     399 source_expressions = clone.get_source_expressions()
     400 clone.set_source_expressions(
     401     [
 --> 402         expr.replace_expressions(replacements) if expr else None
     403         for expr in source_expressions
     404     ]
     405 )
     406 return clone

 File /usr/local/lib/python3.11/site-
 packages/django/db/models/expressions.py:401, in
 BaseExpression.replace_expressions(self, replacements)
     398 clone = self.copy()
     399 source_expressions = clone.get_source_expressions()
     400 clone.set_source_expressions(
 --> 401     [
     402         expr.replace_expressions(replacements) if expr else None
     403         for expr in source_expressions
     404     ]
     405 )
     406 return clone

 File /usr/local/lib/python3.11/site-
 packages/django/db/models/expressions.py:402, in <listcomp>(.0)
     398 clone = self.copy()
     399 source_expressions = clone.get_source_expressions()
     400 clone.set_source_expressions(
     401     [
 --> 402         expr.replace_expressions(replacements) if expr else None
     403         for expr in source_expressions
     404     ]
     405 )
     406 return clone

 AttributeError: 'Q' object has no attribute 'replace_expressions'
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34871>
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/0107018acd3c84e7-49e9c15b-a8cf-4995-8ba9-6de86e559355-000000%40eu-central-1.amazonses.com.

Reply via email to