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." 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(