Fixed #28553 -- Fixed annotation mismatch with QuerySet.values()/values_list() on compound queries.

Co-authored-by: Matthias Kestenholz <mk@feinheit.ch>
This commit is contained in:
David Wobrock 2023-03-13 15:03:28 +01:00 committed by Mariusz Felisiak
parent 39d1e45227
commit d6b6e5d0fd
4 changed files with 31 additions and 9 deletions

View File

@ -1087,7 +1087,12 @@ class Query(BaseExpression):
if select: if select:
self.append_annotation_mask([alias]) self.append_annotation_mask([alias])
else: else:
self.set_annotation_mask(set(self.annotation_select).difference({alias})) annotation_mask = (
value
for value in dict.fromkeys(self.annotation_select)
if value != alias
)
self.set_annotation_mask(annotation_mask)
self.annotations[alias] = annotation self.annotations[alias] = annotation
def resolve_expression(self, query, *args, **kwargs): def resolve_expression(self, query, *args, **kwargs):
@ -2341,12 +2346,12 @@ class Query(BaseExpression):
if names is None: if names is None:
self.annotation_select_mask = None self.annotation_select_mask = None
else: else:
self.annotation_select_mask = set(names) self.annotation_select_mask = list(dict.fromkeys(names))
self._annotation_select_cache = None self._annotation_select_cache = None
def append_annotation_mask(self, names): def append_annotation_mask(self, names):
if self.annotation_select_mask is not None: if self.annotation_select_mask is not None:
self.set_annotation_mask(self.annotation_select_mask.union(names)) self.set_annotation_mask((*self.annotation_select_mask, *names))
def set_extra_mask(self, names): def set_extra_mask(self, names):
""" """
@ -2423,9 +2428,9 @@ class Query(BaseExpression):
return {} return {}
elif self.annotation_select_mask is not None: elif self.annotation_select_mask is not None:
self._annotation_select_cache = { self._annotation_select_cache = {
k: v k: self.annotations[k]
for k, v in self.annotations.items() for k in self.annotation_select_mask
if k in self.annotation_select_mask if k in self.annotations
} }
return self._annotation_select_cache return self._annotation_select_cache
else: else:

View File

@ -295,6 +295,9 @@ Miscellaneous
* Integer fields are now validated as 64-bit integers on SQLite to match the * Integer fields are now validated as 64-bit integers on SQLite to match the
behavior of ``sqlite3``. behavior of ``sqlite3``.
* The undocumented ``Query.annotation_select_mask`` attribute is changed from a
set of strings to an ordered list of strings.
.. _deprecated-features-5.0: .. _deprecated-features-5.0:
Features deprecated in 5.0 Features deprecated in 5.0

View File

@ -466,8 +466,8 @@ class TestQuerying(PostgreSQLTestCase):
], ],
) )
sql = ctx[0]["sql"] sql = ctx[0]["sql"]
self.assertIn("GROUP BY 1", sql) self.assertIn("GROUP BY 2", sql)
self.assertIn("ORDER BY 1", sql) self.assertIn("ORDER BY 2", sql)
def test_index(self): def test_index(self):
self.assertSequenceEqual( self.assertSequenceEqual(

View File

@ -246,7 +246,7 @@ class QuerySetSetOperationTests(TestCase):
) )
.values_list("num", "count") .values_list("num", "count")
) )
self.assertCountEqual(qs1.union(qs2), [(1, 0), (2, 1)]) self.assertCountEqual(qs1.union(qs2), [(1, 0), (1, 2)])
def test_union_with_extra_and_values_list(self): def test_union_with_extra_and_values_list(self):
qs1 = ( qs1 = (
@ -368,6 +368,20 @@ class QuerySetSetOperationTests(TestCase):
[reserved_name.pk], [reserved_name.pk],
) )
def test_union_multiple_models_with_values_list_and_annotations(self):
ReservedName.objects.create(name="rn1", order=10)
Celebrity.objects.create(name="c1")
qs1 = ReservedName.objects.annotate(row_type=Value("rn")).values_list(
"name", "order", "row_type"
)
qs2 = Celebrity.objects.annotate(
row_type=Value("cb"), order=Value(-10)
).values_list("name", "order", "row_type")
self.assertSequenceEqual(
qs1.union(qs2).order_by("order"),
[("c1", -10, "cb"), ("rn1", 10, "rn")],
)
def test_union_in_subquery(self): def test_union_in_subquery(self):
ReservedName.objects.bulk_create( ReservedName.objects.bulk_create(
[ [