mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
This commit is contained in:
committed by
Sarah Boyce
parent
d36ad43f61
commit
d7d711c68c
@@ -1,4 +1,4 @@
|
||||
from .comparison import Cast, Coalesce, Collate, Greatest, JSONObject, Least, NullIf
|
||||
from .comparison import Cast, Coalesce, Collate, Greatest, Least, NullIf
|
||||
from .datetime import (
|
||||
Extract,
|
||||
ExtractDay,
|
||||
@@ -25,6 +25,7 @@ from .datetime import (
|
||||
TruncWeek,
|
||||
TruncYear,
|
||||
)
|
||||
from .json import JSONObject
|
||||
from .math import (
|
||||
Abs,
|
||||
ACos,
|
||||
@@ -97,7 +98,6 @@ __all__ = [
|
||||
"Coalesce",
|
||||
"Collate",
|
||||
"Greatest",
|
||||
"JSONObject",
|
||||
"Least",
|
||||
"NullIf",
|
||||
# datetime
|
||||
@@ -125,6 +125,8 @@ __all__ = [
|
||||
"TruncTime",
|
||||
"TruncWeek",
|
||||
"TruncYear",
|
||||
# json
|
||||
"JSONObject",
|
||||
# math
|
||||
"Abs",
|
||||
"ACos",
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""Database functions that do comparisons or type conversions."""
|
||||
|
||||
from django.db import NotSupportedError
|
||||
from django.db.models.expressions import Func, Value
|
||||
from django.db.models.fields import TextField
|
||||
from django.db.models.fields.json import JSONField
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
|
||||
|
||||
@@ -143,65 +140,6 @@ class Greatest(Func):
|
||||
return super().as_sqlite(compiler, connection, function="MAX", **extra_context)
|
||||
|
||||
|
||||
class JSONObject(Func):
|
||||
function = "JSON_OBJECT"
|
||||
output_field = JSONField()
|
||||
|
||||
def __init__(self, **fields):
|
||||
expressions = []
|
||||
for key, value in fields.items():
|
||||
expressions.extend((Value(key), value))
|
||||
super().__init__(*expressions)
|
||||
|
||||
def as_sql(self, compiler, connection, **extra_context):
|
||||
if not connection.features.has_json_object_function:
|
||||
raise NotSupportedError(
|
||||
"JSONObject() is not supported on this database backend."
|
||||
)
|
||||
return super().as_sql(compiler, connection, **extra_context)
|
||||
|
||||
def join(self, args):
|
||||
pairs = zip(args[::2], args[1::2], strict=True)
|
||||
# Wrap 'key' in parentheses in case of postgres cast :: syntax.
|
||||
return ", ".join([f"({key}) VALUE {value}" for key, value in pairs])
|
||||
|
||||
def as_native(self, compiler, connection, *, returning, **extra_context):
|
||||
return self.as_sql(
|
||||
compiler,
|
||||
connection,
|
||||
arg_joiner=self,
|
||||
template=f"%(function)s(%(expressions)s RETURNING {returning})",
|
||||
**extra_context,
|
||||
)
|
||||
|
||||
def as_postgresql(self, compiler, connection, **extra_context):
|
||||
# Casting keys to text is only required when using JSONB_BUILD_OBJECT
|
||||
# or when using JSON_OBJECT on PostgreSQL 16+ with server-side bindings.
|
||||
# This is done in all cases for consistency.
|
||||
copy = self.copy()
|
||||
copy.set_source_expressions(
|
||||
[
|
||||
Cast(expression, TextField()) if index % 2 == 0 else expression
|
||||
for index, expression in enumerate(copy.get_source_expressions())
|
||||
]
|
||||
)
|
||||
|
||||
if connection.features.is_postgresql_16:
|
||||
return copy.as_native(
|
||||
compiler, connection, returning="JSONB", **extra_context
|
||||
)
|
||||
|
||||
return super(JSONObject, copy).as_sql(
|
||||
compiler,
|
||||
connection,
|
||||
function="JSONB_BUILD_OBJECT",
|
||||
**extra_context,
|
||||
)
|
||||
|
||||
def as_oracle(self, compiler, connection, **extra_context):
|
||||
return self.as_native(compiler, connection, returning="CLOB", **extra_context)
|
||||
|
||||
|
||||
class Least(Func):
|
||||
"""
|
||||
Return the minimum expression.
|
||||
|
||||
64
django/db/models/functions/json.py
Normal file
64
django/db/models/functions/json.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from django.db import NotSupportedError
|
||||
from django.db.models.expressions import Func, Value
|
||||
from django.db.models.fields import TextField
|
||||
from django.db.models.fields.json import JSONField
|
||||
from django.db.models.functions import Cast
|
||||
|
||||
|
||||
class JSONObject(Func):
|
||||
function = "JSON_OBJECT"
|
||||
output_field = JSONField()
|
||||
|
||||
def __init__(self, **fields):
|
||||
expressions = []
|
||||
for key, value in fields.items():
|
||||
expressions.extend((Value(key), value))
|
||||
super().__init__(*expressions)
|
||||
|
||||
def as_sql(self, compiler, connection, **extra_context):
|
||||
if not connection.features.has_json_object_function:
|
||||
raise NotSupportedError(
|
||||
"JSONObject() is not supported on this database backend."
|
||||
)
|
||||
return super().as_sql(compiler, connection, **extra_context)
|
||||
|
||||
def join(self, args):
|
||||
pairs = zip(args[::2], args[1::2], strict=True)
|
||||
# Wrap 'key' in parentheses in case of postgres cast :: syntax.
|
||||
return ", ".join([f"({key}) VALUE {value}" for key, value in pairs])
|
||||
|
||||
def as_native(self, compiler, connection, *, returning, **extra_context):
|
||||
return self.as_sql(
|
||||
compiler,
|
||||
connection,
|
||||
arg_joiner=self,
|
||||
template=f"%(function)s(%(expressions)s RETURNING {returning})",
|
||||
**extra_context,
|
||||
)
|
||||
|
||||
def as_postgresql(self, compiler, connection, **extra_context):
|
||||
# Casting keys to text is only required when using JSONB_BUILD_OBJECT
|
||||
# or when using JSON_OBJECT on PostgreSQL 16+ with server-side bindings.
|
||||
# This is done in all cases for consistency.
|
||||
copy = self.copy()
|
||||
copy.set_source_expressions(
|
||||
[
|
||||
Cast(expression, TextField()) if index % 2 == 0 else expression
|
||||
for index, expression in enumerate(copy.get_source_expressions())
|
||||
]
|
||||
)
|
||||
|
||||
if connection.features.is_postgresql_16:
|
||||
return copy.as_native(
|
||||
compiler, connection, returning="JSONB", **extra_context
|
||||
)
|
||||
|
||||
return super(JSONObject, copy).as_sql(
|
||||
compiler,
|
||||
connection,
|
||||
function="JSONB_BUILD_OBJECT",
|
||||
**extra_context,
|
||||
)
|
||||
|
||||
def as_oracle(self, compiler, connection, **extra_context):
|
||||
return self.as_native(compiler, connection, returning="CLOB", **extra_context)
|
||||
Reference in New Issue
Block a user