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