summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2025-10-13 17:22:17 -0400
committerJacob Walls <jacobtylerwalls@gmail.com>2025-10-14 15:50:26 -0400
commit8baee531d40cd6ee8b342dbcf2c7924d20694969 (patch)
treea4d8c8d0aa4180b20e219d9b3e62d5f57f4d5c38
parent94cbd67d9eb9712be5518c3d5b4c17eac2e63629 (diff)
[5.2.x] Fixed #36648, Refs #33772 -- Accounted for composite pks in first()/last() when aggregating.
Backport of 02eed4f37879b2077496f86bb1378a076b981233 from main.
-rw-r--r--django/db/models/query.py10
-rw-r--r--docs/releases/5.2.8.txt4
-rw-r--r--tests/composite_pk/test_aggregate.py20
3 files changed, 32 insertions, 2 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 1270141619..535d91d767 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -2031,8 +2031,14 @@ class QuerySet(AltersData):
raise TypeError(f"Cannot use {operator_} operator with combined queryset.")
def _check_ordering_first_last_queryset_aggregation(self, method):
- if isinstance(self.query.group_by, tuple) and not any(
- col.output_field is self.model._meta.pk for col in self.query.group_by
+ if (
+ isinstance(self.query.group_by, tuple)
+ # Raise if the pk fields are not in the group_by.
+ and self.model._meta.pk
+ not in {col.output_field for col in self.query.group_by}
+ and set(self.model._meta.pk_fields).difference(
+ {col.target for col in self.query.group_by}
+ )
):
raise TypeError(
f"Cannot use QuerySet.{method}() on an unordered queryset performing "
diff --git a/docs/releases/5.2.8.txt b/docs/releases/5.2.8.txt
index ef18d08022..dc750e4636 100644
--- a/docs/releases/5.2.8.txt
+++ b/docs/releases/5.2.8.txt
@@ -10,3 +10,7 @@ Bugfixes
========
* Added compatibility for ``oracledb`` 3.4.0 (:ticket:`36646`).
+
+* Fixed a bug in Django 5.2 where ``QuerySet.first()`` and ``QuerySet.last()``
+ raised an error on querysets performing aggregation that selected all fields
+ of a composite primary key.
diff --git a/tests/composite_pk/test_aggregate.py b/tests/composite_pk/test_aggregate.py
index d852fdce30..8a2067cb90 100644
--- a/tests/composite_pk/test_aggregate.py
+++ b/tests/composite_pk/test_aggregate.py
@@ -141,3 +141,23 @@ class CompositePKAggregateTests(TestCase):
msg = "Max expression does not support composite primary keys."
with self.assertRaisesMessage(ValueError, msg):
Comment.objects.aggregate(Max("pk"))
+
+ def test_first_from_unordered_queryset_aggregation_pk_selected(self):
+ self.assertEqual(
+ Comment.objects.values("pk").annotate(max=Max("id")).first(),
+ {"pk": (1, 1), "max": 1},
+ )
+
+ def test_first_from_unordered_queryset_aggregation_pk_selected_separately(self):
+ self.assertEqual(
+ Comment.objects.values("tenant", "id").annotate(max=Max("id")).first(),
+ {"tenant": 1, "id": 1, "max": 1},
+ )
+
+ def test_first_from_unordered_queryset_aggregation_pk_incomplete(self):
+ msg = (
+ "Cannot use QuerySet.first() on an unordered queryset performing "
+ "aggregation. Add an ordering with order_by()."
+ )
+ with self.assertRaisesMessage(TypeError, msg):
+ Comment.objects.values("tenant").annotate(max=Max("id")).first()