#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="t...@example.com"))
 }}}

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

 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 django-updates+unsubscr...@googlegroups.com.
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