[Django] #36296: Constraint validation of models with generated fields using Q crashes with AttributeError: 'Q' object has no attribute 'replace_expressions'

4 views
Skip to first unread message

Django

unread,
Apr 3, 2025, 8:30:29 AM4/3/25
to django-...@googlegroups.com
#36296: Constraint validation of models with generated fields using Q crashes with
AttributeError: 'Q' object has no attribute 'replace_expressions'
-------------------------------------+-------------------------------------
Reporter: David Sanders | Type: Bug
Status: new | Component: Database
| layer (models, ORM)
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Given this trivial model

{{{
class Foo(models.Model):
bar = models.CharField()
is_buzz = models.GeneratedField(
expression=models.Q(bar="buzz"),
output_field=models.BooleanField(),
db_persist=True,
)
unrelated = models.CharField()

class Meta:
constraints = [
models.CheckConstraint(
name="check_unrelated",
condition=models.Q(unrelated="unrelated")
),
]
}}}

Running `validate_constraints()` will raise an AttributeError (see below)

This was previously working on 5.1, it appears to be cause by the
introduction of constraint validation on generated fields themselves in
https://github.com/django/django/commit/228128618bd

{{{
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../django/django/db/models/base.py:1576: in validate_constraints
constraint.validate(model_class, self, exclude=exclude, using=using)
../../django/django/db/models/constraints.py:602: in validate
field_expression_map = instance._get_field_expression_map(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <Foo: Foo object (None)>, meta = <Options for Foo>, exclude = set()

def _get_field_expression_map(self, meta, exclude=None):
if exclude is None:
exclude = set()
meta = meta or self._meta
field_map = {}
generated_fields = []
for field in meta.local_concrete_fields:
if field.name in exclude:
continue
if field.generated:
if any(
ref[0] in exclude
for ref in self._get_expr_references(field.expression)
):
continue
generated_fields.append(field)
continue
value = getattr(self, field.attname)
if not value or not hasattr(value, "resolve_expression"):
value = Value(value, field)
field_map[field.name] = value
if "pk" not in exclude:
field_map["pk"] = Value(self.pk, meta.pk)
if generated_fields:
replacements = {F(name): value for name, value in
field_map.items()}
for generated_field in generated_fields:
field_map[generated_field.name] = ExpressionWrapper(
>
generated_field.expression.replace_expressions(replacements),
generated_field.output_field,
)
E AttributeError: 'Q' object has no attribute
'replace_expressions'

../../django/django/db/models/base.py:1326: AttributeError
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36296>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Apr 3, 2025, 9:47:38 AM4/3/25
to django-...@googlegroups.com
#36296: Constraint validation of models with generated fields using Q crashes with
AttributeError: 'Q' object has no attribute 'replace_expressions'
-------------------------------------+-------------------------------------
Reporter: David Sanders | Owner: (none)
Type: Bug | Status: new
Component: Database layer | Version: 5.2
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Simon Charette):

This relates to #34871 which has
[https://github.com/django/django/pull/19190/ a working prototype] that
implements `Q.replace_expressions`.

Note that passing a `Q` to `GeneratedFied.expression` was never entirely
supported #34805 (as it's not an expression per-se) and passing a lookup
instead most likely solves your issue

{{{#!python
is_buzz = models.GeneratedField(
expression=Exact(F("bar"), Value("buzz")),
output_field=models.BooleanField(),
db_persist=True,
)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36296#comment:1>

Django

unread,
Apr 3, 2025, 10:55:46 AM4/3/25
to django-...@googlegroups.com
#36296: Constraint validation of models with generated fields using Q crashes with
AttributeError: 'Q' object has no attribute 'replace_expressions'
-------------------------------------+-------------------------------------
Reporter: David Sanders | Owner: (none)
Type: Bug | Status: closed
Component: Database layer | Version: 5.2
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Sarah Boyce):

* resolution: => duplicate
* status: new => closed

Comment:

Thank you for the information Simon
Marking as a duplicate as I think #34871 and #34805 cover this request,
feel free to reopen if you disagree
--
Ticket URL: <https://code.djangoproject.com/ticket/36296#comment:2>
Reply all
Reply to author
Forward
0 new messages