[3.1.x] Fixed #31566 -- Fixed aliases crash when chaining values()/values_list() after annotate() with aggregations and subqueries.

Subquery annotation references must be resolved if they are excluded
from the GROUP BY clause by a following .values() call.

Regression in fb3f034f1c.

Thanks Makina Corpus for the report.

Backport of 42c08ee465 from master
This commit is contained in:
Simon Charette 2020-05-13 23:38:29 -04:00 committed by Mariusz Felisiak
parent 6227173542
commit 8cb87a3f7c
3 changed files with 31 additions and 2 deletions

View File

@ -2155,6 +2155,15 @@ class Query(BaseExpression):
# SELECT clause which is about to be cleared. # SELECT clause which is about to be cleared.
self.set_group_by(allow_aliases=False) self.set_group_by(allow_aliases=False)
self.clear_select_fields() self.clear_select_fields()
elif self.group_by:
# Resolve GROUP BY annotation references if they are not part of
# the selected fields anymore.
group_by = []
for expr in self.group_by:
if isinstance(expr, Ref) and expr.refs not in field_names:
expr = self.annotations[expr.refs]
group_by.append(expr)
self.group_by = tuple(group_by)
self.values_select = tuple(field_names) self.values_select = tuple(field_names)
self.add_fields(field_names, True) self.add_fields(field_names, True)

View File

@ -11,3 +11,7 @@ Bugfixes
* Fixed a regression in Django 3.0 by restoring the ability to use field * Fixed a regression in Django 3.0 by restoring the ability to use field
lookups in ``Meta.ordering`` (:ticket:`31538`). lookups in ``Meta.ordering`` (:ticket:`31538`).
* Fixed a regression in Django 3.0 where ``QuerySet.values()`` and
``values_list()`` crashed if a queryset contained an aggregation and a
subquery annotation (:ticket:`31566`).

View File

@ -1,10 +1,13 @@
import datetime import datetime
from decimal import Decimal from decimal import Decimal
from unittest import skipIf
from django.core.exceptions import FieldDoesNotExist, FieldError from django.core.exceptions import FieldDoesNotExist, FieldError
from django.db import connection
from django.db.models import ( from django.db.models import (
BooleanField, CharField, Count, DateTimeField, ExpressionWrapper, F, Func, BooleanField, CharField, Count, DateTimeField, Exists, ExpressionWrapper,
IntegerField, NullBooleanField, OuterRef, Q, Subquery, Sum, Value, F, Func, IntegerField, Max, NullBooleanField, OuterRef, Q, Subquery, Sum,
Value,
) )
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from django.db.models.functions import Length, Lower from django.db.models.functions import Length, Lower
@ -619,3 +622,16 @@ class NonAggregateAnnotationTestCase(TestCase):
total_books=Subquery(long_books_qs, output_field=IntegerField()), total_books=Subquery(long_books_qs, output_field=IntegerField()),
).values('name') ).values('name')
self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}]) self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}])
@skipIf(connection.vendor == 'oracle', 'See https://code.djangoproject.com/ticket/31584')
def test_annotation_exists_aggregate_values_chaining(self):
qs = Book.objects.values('publisher').annotate(
has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),
max_pubdate=Max('pubdate'),
).values_list('max_pubdate', flat=True).order_by('max_pubdate')
self.assertCountEqual(qs, [
datetime.date(1991, 10, 15),
datetime.date(2008, 3, 3),
datetime.date(2008, 6, 23),
datetime.date(2008, 11, 3),
])