1
0
mirror of https://github.com/django/django.git synced 2025-08-23 10:19:13 +00:00

[3.1.x] Fixed #31614 -- Fixed aliases ordering by OrderBy() expressions of combined queryset.

Backport of 2aac176e86204785f0f2ec4838049d8fed70870e from master
This commit is contained in:
Laurent Tramoy 2020-05-26 09:11:11 +02:00 committed by Mariusz Felisiak
parent 114da2d045
commit df88f24b1f
2 changed files with 22 additions and 2 deletions

View File

@ -6,7 +6,7 @@ from itertools import chain
from django.core.exceptions import EmptyResultSet, FieldError from django.core.exceptions import EmptyResultSet, FieldError
from django.db import DatabaseError, NotSupportedError from django.db import DatabaseError, NotSupportedError
from django.db.models.constants import LOOKUP_SEP 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.functions import Cast
from django.db.models.query_utils import Q, select_related_descend from django.db.models.query_utils import Q, select_related_descend
from django.db.models.sql.constants import ( from django.db.models.sql.constants import (
@ -361,13 +361,16 @@ class SQLCompiler:
resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None)
if self.query.combinator: if self.query.combinator:
src = resolved.get_source_expressions()[0] 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 # Relabel order by columns to raw numbers if this is a combined
# query; necessary since the columns can't be referenced by the # query; necessary since the columns can't be referenced by the
# fully qualified name and the simple column names may collide. # fully qualified name and the simple column names may collide.
for idx, (sel_expr, _, col_alias) in enumerate(self.select): for idx, (sel_expr, _, col_alias) in enumerate(self.select):
if is_ref and col_alias == src.refs: if is_ref and col_alias == src.refs:
src = src.source src = src.source
elif col_alias: elif col_alias and not (
isinstance(expr_src, F) and col_alias == expr_src.name
):
continue continue
if src == sel_expr: if src == sel_expr:
resolved.set_source_expressions([RawSQL('%d' % (idx + 1), ())]) resolved.set_source_expressions([RawSQL('%d' % (idx + 1), ())])

View File

@ -1,3 +1,5 @@
import operator
from django.db import DatabaseError, NotSupportedError, connection from django.db import DatabaseError, NotSupportedError, connection
from django.db.models import Exists, F, IntegerField, OuterRef, Value from django.db.models import Exists, F, IntegerField, OuterRef, Value
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
@ -115,6 +117,21 @@ class QuerySetSetOperationTests(TestCase):
qs2 = Number.objects.filter(num__gte=2, num__lte=3) qs2 = Number.objects.filter(num__gte=2, num__lte=3)
self.assertNumbersEqual(qs1.union(qs2).order_by(F('num').desc()), [3, 2, 1, 0]) 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): def test_union_with_values(self):
ReservedName.objects.create(name='a', order=2) ReservedName.objects.create(name='a', order=2)
qs1 = ReservedName.objects.all() qs1 = ReservedName.objects.all()