diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index dd356181e331..50fa2ce7e955 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -76,7 +76,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): requires_compound_order_by_subquery = True allows_multiple_constraints_on_same_fields = False supports_json_field_contains = False - supports_json_negative_indexing = False supports_collation_on_textfield = False supports_on_delete_db_default = False supports_no_precision_decimalfield = True @@ -91,6 +90,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): "INSERT INTO {} VALUES (DEFAULT, DEFAULT, DEFAULT)" ) + @cached_property + def supports_json_negative_indexing(self): + return self.connection.oracle_version >= (21,) + @cached_property def django_test_skips(self): skips = { diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index 1da4fe6127b5..b3e28b102ffc 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -729,3 +729,8 @@ def conditional_expression_supported_in_where_clause(self, expression): if isinstance(expression, RawSQL) and expression.conditional: return True return False + + def format_json_path_numeric_index(self, num): + if num < 0: + return "[last-%s]" % abs(num + 1) # Indexing is zero-based. + return super().format_json_path_numeric_index(num) diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 41b554bb5d24..ed1855cac941 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -254,6 +254,10 @@ Models ` are no longer required to be set on Oracle, PostgreSQL, and SQLite. +* :class:`~django.db.models.JSONField` now supports + :ref:`negative array indexing ` on Oracle + 21c+. + Pagination ~~~~~~~~~~ diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index 0a63ffb55354..4070ab9066bd 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1184,7 +1184,7 @@ directly, but you can still use dictionary unpacking to use it in a query: >>> Dog.objects.filter(**{"data__owner__other_pets__-1__name": "Fishy"}) ]> -.. admonition:: MySQL, MariaDB, and Oracle +.. admonition:: MySQL, MariaDB, and Oracle < 21c Negative JSON array indices are not supported. @@ -1192,6 +1192,10 @@ directly, but you can still use dictionary unpacking to use it in a query: SQLite support for negative JSON array indices was added. +.. versionchanged:: 6.1 + + Oracle 21c+ support for negative JSON array indices was added. + If the key you wish to query by clashes with the name of another lookup, use the :lookup:`contains ` lookup instead.