1
0
mirror of https://github.com/django/django.git synced 2025-04-01 03:56:42 +00:00

[3.1.x] Fixed -- 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 fb3f034f1c63160c0ff13c609acd01c18be12f80.

Thanks Makina Corpus for the report.

Backport of 42c08ee46539ef44f8658ebb1cbefb408e0d03fe 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
django/db/models/sql
docs/releases
tests/annotations

@ -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)

@ -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`).

@ -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),
])