From c6ace896a2da73356f7c9a655bbe32a0e3ce0435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vinko=20Mla=C4=8Di=C4=87?= Date: Tue, 28 Jan 2025 22:57:32 +0100 Subject: [PATCH] Fixed #36155 -- Improved error handling when annotate arguments require an alias. Regression in ed0cbc8d8b314e3b4a0305d0be3cf366d8ee4a74. --- AUTHORS | 1 + django/db/models/query.py | 6 ++++-- tests/annotations/tests.py | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0648241267..9a76e8879e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1053,6 +1053,7 @@ answer newbie questions, and generally made Django that much better: Vinay Karanam Vinay Sajip Vincent Foley + Vinko Mlačić Vinny Do Vitaly Babiy Vitaliy Yelnik diff --git a/django/db/models/query.py b/django/db/models/query.py index 610e496e9f..4aa7f03a5f 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1646,14 +1646,16 @@ class QuerySet(AltersData): ) annotations = {} for arg in args: - # The default_alias property may raise a TypeError. + # The default_alias property raises TypeError if default_alias + # can't be set automatically or AttributeError if it isn't an + # attribute. try: if arg.default_alias in kwargs: raise ValueError( "The named annotation '%s' conflicts with the " "default name for another annotation." % arg.default_alias ) - except TypeError: + except (TypeError, AttributeError): raise TypeError("Complex annotations require an alias") annotations[arg.default_alias] = arg annotations.update(kwargs) diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index 29660a827e..5df958c333 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -969,6 +969,24 @@ class NonAggregateAnnotationTestCase(TestCase): ): Book.objects.annotate(BooleanField(), Value(False), is_book=True) + def test_complex_annotations_must_have_an_alias(self): + complex_annotations = [ + F("rating") * F("price"), + Value("title"), + Case(When(pages__gte=400, then=Value("Long")), default=Value("Short")), + Subquery( + Book.objects.filter(publisher_id=OuterRef("pk")) + .order_by("-pubdate") + .values("name")[:1] + ), + Exists(Book.objects.filter(publisher_id=OuterRef("pk"))), + ] + msg = "Complex annotations require an alias" + for annotation in complex_annotations: + with self.subTest(annotation=annotation): + with self.assertRaisesMessage(TypeError, msg): + Book.objects.annotate(annotation) + def test_chaining_annotation_filter_with_m2m(self): qs = ( Author.objects.filter(