Browse Source

Fixed #31614 -- Fixed aliases ordering by OrderBy() expressions of combined queryset.

Laurent Tramoy 4 years ago
parent
commit
2aac176e86
2 changed files with 20 additions and 2 deletions
  1. 5 2
      django/db/models/sql/compiler.py
  2. 15 0
      tests/queries/test_qs_combinators.py

+ 5 - 2
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), ())])

+ 15 - 0
tests/queries/test_qs_combinators.py

@@ -123,6 +123,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()