From a8716f3c4c6d78d38ce86d79c816062346dcc5bf Mon Sep 17 00:00:00 2001 From: hesham942 Date: Tue, 13 May 2025 13:37:02 +0200 Subject: [PATCH] Refs #36085 -- Moved compile_json_path to BaseDatabaseOperations. --- django/db/backends/base/operations.py | 15 ++++++++++++ django/db/models/fields/json.py | 35 +++++++++------------------ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index fea73bc1e4..ba6650ed69 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -792,3 +792,18 @@ class BaseDatabaseOperations: def format_debug_sql(self, sql): # Hook for backends (e.g. NoSQL) to customize formatting. return sqlparse.format(sql, reindent=True, keyword_case="upper") + + def compile_json_path(self, key_transforms, include_root=True): + """ + Hook for backends to customize all aspects of JSON path construction. + """ + path = ["$"] if include_root else [] + for key_transform in key_transforms: + try: + num = int(key_transform) + except ValueError: # Non-integer. + path.append(".") + path.append(json.dumps(key_transform)) + else: + path.append("[%s]" % num) + return "".join(path) diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py index 2fc1abb032..d544caf3e5 100644 --- a/django/db/models/fields/json.py +++ b/django/db/models/fields/json.py @@ -148,19 +148,6 @@ class JSONField(CheckFieldDefaultMixin, Field): ) -def compile_json_path(key_transforms, include_root=True): - path = ["$"] if include_root else [] - for key_transform in key_transforms: - try: - num = int(key_transform) - except ValueError: # non-integer - path.append(".") - path.append(json.dumps(key_transform)) - else: - path.append("[%s]" % num) - return "".join(path) - - class DataContains(FieldGetDbPrepValueMixin, PostgresOperatorLookup): lookup_name = "contains" postgres_operator = "@>" @@ -194,7 +181,7 @@ class ContainedBy(FieldGetDbPrepValueMixin, PostgresOperatorLookup): class HasKeyLookup(PostgresOperatorLookup): logical_operator = None - def compile_json_path_final_key(self, key_transform): + def compile_json_path_final_key(self, connection, key_transform): # Compile the final key without interpreting ints as array elements. return ".%s" % json.dumps(key_transform) @@ -204,7 +191,7 @@ class HasKeyLookup(PostgresOperatorLookup): lhs_sql, lhs_params, lhs_key_transforms = self.lhs.preprocess_lhs( compiler, connection ) - lhs_json_path = compile_json_path(lhs_key_transforms) + lhs_json_path = connection.ops.compile_json_path(lhs_key_transforms) else: lhs_sql, lhs_params = self.process_lhs(compiler, connection) lhs_json_path = "$" @@ -218,8 +205,10 @@ class HasKeyLookup(PostgresOperatorLookup): else: rhs_key_transforms = [key] *rhs_key_transforms, final_key = rhs_key_transforms - rhs_json_path = compile_json_path(rhs_key_transforms, include_root=False) - rhs_json_path += self.compile_json_path_final_key(final_key) + rhs_json_path = connection.ops.compile_json_path( + rhs_key_transforms, include_root=False + ) + rhs_json_path += self.compile_json_path_final_key(connection, final_key) yield lhs_sql, lhs_params, lhs_json_path + rhs_json_path def _combine_sql_parts(self, parts): @@ -296,8 +285,8 @@ class HasAnyKeys(HasKeys): class HasKeyOrArrayIndex(HasKey): - def compile_json_path_final_key(self, key_transform): - return compile_json_path([key_transform], include_root=False) + def compile_json_path_final_key(self, connection, key_transform): + return connection.ops.compile_json_path([key_transform], include_root=False) class CaseInsensitiveMixin: @@ -378,12 +367,12 @@ class KeyTransform(Transform): def as_mysql(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) - json_path = compile_json_path(key_transforms) + json_path = connection.ops.compile_json_path(key_transforms) return "JSON_EXTRACT(%s, %%s)" % lhs, (*params, json_path) def as_oracle(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) - json_path = compile_json_path(key_transforms) + json_path = connection.ops.compile_json_path(key_transforms) if connection.features.supports_primitives_in_json_field: sql = ( "COALESCE(" @@ -419,7 +408,7 @@ class KeyTransform(Transform): def as_sqlite(self, compiler, connection): lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) - json_path = compile_json_path(key_transforms) + json_path = connection.ops.compile_json_path(key_transforms) datatype_values = ",".join( [repr(datatype) for datatype in connection.ops.jsonfield_datatype_values] ) @@ -441,7 +430,7 @@ class KeyTextTransform(KeyTransform): return "JSON_UNQUOTE(%s)" % sql, params else: lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) - json_path = compile_json_path(key_transforms) + json_path = connection.ops.compile_json_path(key_transforms) return "(%s ->> %%s)" % lhs, (*params, json_path) @classmethod