From 439b936753e5a679ffcf40cff5918c5637f7c72b Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 29 Aug 2026 09:10:23 -0500 Subject: [PATCH 1/2] Added test skip for 1-element GeometryCollections on Oracle 23.9. --- django/contrib/gis/db/backends/oracle/features.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django/contrib/gis/db/backends/oracle/features.py b/django/contrib/gis/db/backends/oracle/features.py index eae6fcfbd03b..9e9ea84cdad4 100644 --- a/django/contrib/gis/db/backends/oracle/features.py +++ b/django/contrib/gis/db/backends/oracle/features.py @@ -33,6 +33,8 @@ def django_test_skips(self): skips.update( { "Oracle 23ai unpacks 1-element geometry collections.": { + "gis_tests.geogapp.tests.GeographyTest." + "test05_geography_layermapping", "gis_tests.layermap.tests.LayerMapTest." "test_layermap_unique_multigeometry_fk", "gis_tests.layermap.tests.LayerMapTest." From 73cc09f14f13fedddc14d6ba5b287cb33c24e4a4 Mon Sep 17 00:00:00 2001 From: Annabelle Wiegart Date: Wed, 19 Aug 2026 18:47:21 +0200 Subject: [PATCH 2/2] Fixed #36945 -- Made .values() and .order_by() resolve FilteredRelation aliases correctly. Made Query.add_fields(), Query.add_ordering() and SQLCompiler.find_ordering_name() look up FilteredRelation aliases before splitting field names to relationship traversals. Added .test_alias_values() and .test_alias_order_by() to FilteredRelationTests. Thanks Pravin Kamble for the review. --- django/db/models/sql/compiler.py | 2 +- django/db/models/sql/query.py | 13 +++++++++++-- tests/filtered_relation/tests.py | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 4972f29d61a7..bbb83dee045f 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1073,7 +1073,7 @@ def find_ordering_name( """ name, order = get_order_dir(name, default_order) descending = order == "DESC" - pieces = name.split(LOOKUP_SEP) + pieces = self.query.get_names_to_join(name) ( field, targets, diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index e3b97ec425bf..204a8e3a52f9 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1485,6 +1485,13 @@ def try_transform(self, lhs, name, lookups=None): "permitted%s" % (unsupported_lookup, output_field.__name__, suggestion) ) + def get_names_to_join(self, expr): + """ + Helper method for the resolution of expressions that could either be a + FilteredRelation alias or a field lookup. + """ + return [expr] if expr in self._filtered_relations else expr.split(LOOKUP_SEP) + def build_filter( self, filter_expr, @@ -2276,10 +2283,11 @@ def add_fields(self, field_names, allow_m2m=True): try: cols = [] for name in field_names: + names_to_join = self.get_names_to_join(name) # Join promotion note - we must not remove any rows here, so # if there is no existing joins, use outer join. join_info = self.setup_joins( - name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m + names_to_join, opts, alias, allow_many=allow_m2m ) targets, final_alias, joins = self.trim_joins( join_info.targets, @@ -2343,9 +2351,10 @@ def add_ordering(self, *ordering): continue if self.extra and item in self.extra: continue + names_to_join = self.get_names_to_join(item) # names_to_path() validates the lookup. A descriptive # FieldError will be raise if it's not. - self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) + self.names_to_path(names_to_join, self.model._meta) elif not hasattr(item, "resolve_expression"): errors.append(item) if getattr(item, "contains_aggregate", False): diff --git a/tests/filtered_relation/tests.py b/tests/filtered_relation/tests.py index 9047feba2d01..ed224418b71f 100644 --- a/tests/filtered_relation/tests.py +++ b/tests/filtered_relation/tests.py @@ -358,6 +358,29 @@ def test_values(self): ], ) + def test_lookup_sep_in_alias_values(self): + alias = "editor__name" + queryset = Book.objects.annotate( + **{alias: FilteredRelation("editor", condition=Q(editor__isnull=False))} + ).filter(title="The book by Alice") + self.assertSequenceEqual(queryset.values(alias), [{alias: self.editor_a.pk}]) + self.assertSequenceEqual(queryset.values_list(alias), [(self.editor_a.pk,)]) + + def test_lookup_sep_in_alias_order_by(self): + alias = "editor__name" + editor_d = Editor.objects.create(name="d") + editor_c = Editor.objects.create(name="c") + book5 = Book.objects.create( + title="Poems from outer space", editor=editor_d, author=self.author1 + ) + book6 = Book.objects.create( + title="Stories from outer space", editor=editor_c, author=self.author1 + ) + queryset = Book.objects.annotate( + **{alias: FilteredRelation("editor", condition=Q(editor__isnull=False))} + ).filter(title__contains="outer space") + self.assertSequenceEqual(queryset.order_by(alias), [book5, book6]) + def test_extra(self): self.assertSequenceEqual( Author.objects.annotate(