#30542 closed Bug (fixed)
Float-valued aggregations and annotations with filters fail with AttributeError
Reported by: | Chuan-Zheng Lee | Owned by: | Étienne Beaulé |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 2.2 |
Severity: | Release blocker | Keywords: | aggregation, annotation, filter |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When any float-valued aggregation or annotation (Avg
, StdDev
, Variance
) is used with the filter=
keyword argument, the following exception is raised:
AttributeError: 'WhereNode' object has no attribute 'output_field'
For example, these queries all fail with this error:
Speaker.objects.annotate(average=Avg('speakerscore__score', filter=Q(speakerscore__ghost=False))) Speaker.objects.annotate(average=StdDev('speakerscore__score', filter=Q(speakerscore__ghost=False))) Team.objects.annotate(average=Avg('debateteam__teamscore__score', filter=Q(debateteam__teamscore__forfeit=False)))
The error seems to be raised irrespective of what's in the database (e.g., it's raised even for an empty database). However, it doesn't affect aggregations like Sum
, Max
or Min
that don't use NumericOutputFieldMixin
. Also, queries that don't use the filter=
keyword in the aggregation work fine.
The exception in question is raised from line 46 of django/db/models/functions/mixins.py, which looks for an output_field
attribute of every element of self.get_source_expressions()
, where self
is the object containing NumericOutputFieldMixin
, in this case Avg
or some other subclass of Aggregate
. But Aggregate.get_source_expressions()
includes self.filter
in its list, and (post-compilation) self.filter
is a WhereNode
, which doesn't have an output_field
attribute.
This issue is new in version 2.2. Everything works fine in version 2.1 (where I believe NumericOutputFieldMixin
didn't exist, or at least wasn't on the MRO for Avg
, StdDev
or Variance
).
Minimal reproducible example
In a blank (or any) project, insert in models.py:
from django.db.models import Model, FloatField, BooleanField class Book(Model): price = FloatField() fiction = BooleanField()
then run migrations and in python manage.py shell
:
$ python manage.py shell Python 3.6.7 (default, Oct 22 2018, 11:32:17) [GCC 8.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django22_output_field_bug.models import Book >>> from django.db.models import Avg, Q >>> Book.objects.annotate(average=Avg('price', filter=Q(fiction=True))) Traceback (most recent call last): [...] File "/[...]/django/db/models/functions/mixins.py", line 46, in <genexpr> if any(isinstance(s.output_field, DecimalField) for s in source_expressions): AttributeError: 'WhereNode' object has no attribute 'output_field' >>> Book.objects.aggregate(average=Avg('price', filter=Q(fiction=True))) Traceback (most recent call last): [...] File "/[...]/django/db/models/functions/mixins.py", line 46, in <genexpr> if any(isinstance(s.output_field, DecimalField) for s in source_expressions): AttributeError: 'WhereNode' object has no attribute 'output_field'
Change History (6)
comment:2 by , 5 years ago
Has patch: | set |
---|---|
Owner: | changed from | to
Status: | new → assigned |
Triage Stage: | Unreviewed → Accepted |
Version: | 2.2 → 2.0 |
comment:3 by , 5 years ago
Contrary to my earlier guess, the first commit with the issue is c690afb873cac8035a3cb3be7c597a5ff0e4b261, which modifies Avg
to use the new logic. Its parent, 3d5e0f8394688d40036e27cfcfac295e6fe62269, works fine, because while it modified the mixin to its current form, it didn't touch Avg
.
comment:4 by , 5 years ago
Severity: | Normal → Release blocker |
---|---|
Version: | 2.0 → 2.2 |
I couldn't tell you for sure because I haven't actually tried reproducing it on this commit or its parent, but if I had to take a guess as to which commit introduced the issue, I'd try a0b19a0f5b1731cf575546175034da53f5af5367, which introduced→ see #comment:3OutputFieldMixin
, a precursor toNumericOutputFieldMixin
with similar logic. I'll say something if I manage to give this a go.