summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wobrock <david.wobrock@gmail.com>2023-03-13 15:03:28 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-03-24 06:09:27 +0100
commitd6b6e5d0fd4e6b6d0183b4cf6e4bd4f9afc7bf67 (patch)
tree1e81239741fa0b7a576879c42040953ed64e9708
parent39d1e45227e060746ed461fddde80fa2b6cf0dcd (diff)
Fixed #28553 -- Fixed annotation mismatch with QuerySet.values()/values_list() on compound queries.
Co-authored-by: Matthias Kestenholz <mk@feinheit.ch>
-rw-r--r--django/db/models/sql/query.py17
-rw-r--r--docs/releases/5.0.txt3
-rw-r--r--tests/postgres_tests/test_array.py4
-rw-r--r--tests/queries/test_qs_combinators.py16
4 files changed, 31 insertions, 9 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 103fffa47d..9865e88fd0 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1087,7 +1087,12 @@ class Query(BaseExpression):
if select:
self.append_annotation_mask([alias])
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
def resolve_expression(self, query, *args, **kwargs):
@@ -2341,12 +2346,12 @@ class Query(BaseExpression):
if names is None:
self.annotation_select_mask = None
else:
- self.annotation_select_mask = set(names)
+ self.annotation_select_mask = list(dict.fromkeys(names))
self._annotation_select_cache = None
def append_annotation_mask(self, names):
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):
"""
@@ -2423,9 +2428,9 @@ class Query(BaseExpression):
return {}
elif self.annotation_select_mask is not None:
self._annotation_select_cache = {
- k: v
- for k, v in self.annotations.items()
- if k in self.annotation_select_mask
+ k: self.annotations[k]
+ for k in self.annotation_select_mask
+ if k in self.annotations
}
return self._annotation_select_cache
else:
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index 385f4a6512..b2e8b9d48b 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -295,6 +295,9 @@ Miscellaneous
* Integer fields are now validated as 64-bit integers on SQLite to match the
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:
Features deprecated in 5.0
diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py
index f7615c974e..5f8441ba1b 100644
--- a/tests/postgres_tests/test_array.py
+++ b/tests/postgres_tests/test_array.py
@@ -466,8 +466,8 @@ class TestQuerying(PostgreSQLTestCase):
],
)
sql = ctx[0]["sql"]
- self.assertIn("GROUP BY 1", sql)
- self.assertIn("ORDER BY 1", sql)
+ self.assertIn("GROUP BY 2", sql)
+ self.assertIn("ORDER BY 2", sql)
def test_index(self):
self.assertSequenceEqual(
diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py
index bf127ce703..71d2418f2b 100644
--- a/tests/queries/test_qs_combinators.py
+++ b/tests/queries/test_qs_combinators.py
@@ -246,7 +246,7 @@ class QuerySetSetOperationTests(TestCase):
)
.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):
qs1 = (
@@ -368,6 +368,20 @@ class QuerySetSetOperationTests(TestCase):
[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):
ReservedName.objects.bulk_create(
[