From df88f24b1f5805fc388a2184e56b964746608bf4 Mon Sep 17 00:00:00 2001 From: Laurent Tramoy Date: Tue, 26 May 2020 09:11:11 +0200 Subject: [PATCH] [3.1.x] Fixed #31614 -- Fixed aliases ordering by OrderBy() expressions of combined queryset. Backport of 2aac176e86204785f0f2ec4838049d8fed70870e from master --- django/db/models/sql/compiler.py | 7 +++++-- tests/queries/test_qs_combinators.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 2c51561ae8c..e6e840d8ead 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -6,7 +6,7 @@ from itertools import chain from django.core.exceptions import EmptyResultSet, FieldError from django.db import DatabaseError, NotSupportedError from django.db.models.constants import LOOKUP_SEP -from django.db.models.expressions import OrderBy, Random, RawSQL, Ref, Value +from django.db.models.expressions import F, OrderBy, Random, RawSQL, Ref, Value from django.db.models.functions import Cast from django.db.models.query_utils import Q, select_related_descend from django.db.models.sql.constants import ( @@ -361,13 +361,16 @@ class SQLCompiler: resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) if self.query.combinator: src = resolved.get_source_expressions()[0] + expr_src = expr.get_source_expressions()[0] # Relabel order by columns to raw numbers if this is a combined # query; necessary since the columns can't be referenced by the # fully qualified name and the simple column names may collide. for idx, (sel_expr, _, col_alias) in enumerate(self.select): if is_ref and col_alias == src.refs: src = src.source - elif col_alias: + elif col_alias and not ( + isinstance(expr_src, F) and col_alias == expr_src.name + ): continue if src == sel_expr: resolved.set_source_expressions([RawSQL('%d' % (idx + 1), ())]) diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py index 1d159e1fed6..bd8b0c309f0 100644 --- a/tests/queries/test_qs_combinators.py +++ b/tests/queries/test_qs_combinators.py @@ -1,3 +1,5 @@ +import operator + from django.db import DatabaseError, NotSupportedError, connection from django.db.models import Exists, F, IntegerField, OuterRef, Value from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature @@ -115,6 +117,21 @@ class QuerySetSetOperationTests(TestCase): qs2 = Number.objects.filter(num__gte=2, num__lte=3) self.assertNumbersEqual(qs1.union(qs2).order_by(F('num').desc()), [3, 2, 1, 0]) + def test_ordering_by_f_expression_and_alias(self): + qs1 = Number.objects.filter(num__lte=1).values(alias=F('other_num')) + qs2 = Number.objects.filter(num__gte=2, num__lte=3).values(alias=F('other_num')) + self.assertQuerysetEqual( + qs1.union(qs2).order_by(F('alias').desc()), + [10, 9, 8, 7], + operator.itemgetter('alias'), + ) + Number.objects.create(num=-1) + self.assertQuerysetEqual( + qs1.union(qs2).order_by(F('alias').desc(nulls_last=True)), + [10, 9, 8, 7, None], + operator.itemgetter('alias'), + ) + def test_union_with_values(self): ReservedName.objects.create(name='a', order=2) qs1 = ReservedName.objects.all()