From 68c9f7e0b79168007e6ba0139fd315d7c44ca8c9 Mon Sep 17 00:00:00 2001 From: ontowhee <82607723+ontowhee@users.noreply.github.com> Date: Thu, 22 May 2025 11:06:28 -0700 Subject: [PATCH] Fixed #36407 -- Ensured default value is cast in Case expressions used in ORDER BY clause. Thanks to deceze for the report. Thanks to Sarah Boyce for the test. Thanks to Simon Charette for the investigation and review. --- django/db/models/expressions.py | 18 ++++++++++++++---- tests/ordering/tests.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 46c3b63a91..54e372ea01 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1711,14 +1711,24 @@ class Case(SQLiteNumericMixin, Expression): except EmptyResultSet: continue except FullResultSet: - default_sql, default_params = compiler.compile(case.result) + default = case.result break case_parts.append(case_sql) sql_params.extend(case_params) else: - default_sql, default_params = compiler.compile(self.default) - if not case_parts: - return default_sql, default_params + default = self.default + if case_parts: + default_sql, default_params = compiler.compile(default) + else: + if ( + isinstance(default, Value) + and (output_field := default._output_field_or_none) is not None + ): + from django.db.models.functions import Cast + + default = Cast(default, output_field) + return compiler.compile(default) + case_joiner = case_joiner or self.case_joiner template_params["cases"] = case_joiner.join(case_parts) template_params["default"] = default_sql diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py index b29404ed77..421689b9fa 100644 --- a/tests/ordering/tests.py +++ b/tests/ordering/tests.py @@ -3,15 +3,18 @@ from operator import attrgetter from django.core.exceptions import FieldError from django.db.models import ( + Case, CharField, Count, DateTimeField, F, + IntegerField, Max, OrderBy, OuterRef, Subquery, Value, + When, ) from django.db.models.functions import Length, Upper from django.test import TestCase @@ -526,6 +529,17 @@ class OrderingTests(TestCase): qs = Article.objects.order_by(Value("1", output_field=CharField()), "-headline") self.assertSequenceEqual(qs, [self.a4, self.a3, self.a2, self.a1]) + def test_order_by_case_when_constant_value(self): + qs = Article.objects.order_by( + Case( + When(pk__in=[], then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ).desc(), + "pk", + ) + self.assertSequenceEqual(qs, [self.a1, self.a2, self.a3, self.a4]) + def test_related_ordering_duplicate_table_reference(self): """ An ordering referencing a model with an ordering referencing a model