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