1
0
mirror of https://github.com/django/django.git synced 2025-03-13 10:50:55 +00:00

Refs #35718, Refs #32179 -- Moved JSONObject to django.db.models.functions.json.

This commit is contained in:
Sage Abdullah 2024-12-15 16:01:45 +00:00 committed by Sarah Boyce
parent d36ad43f61
commit d7d711c68c
6 changed files with 98 additions and 89 deletions

View File

@ -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 ( from .datetime import (
Extract, Extract,
ExtractDay, ExtractDay,
@ -25,6 +25,7 @@ from .datetime import (
TruncWeek, TruncWeek,
TruncYear, TruncYear,
) )
from .json import JSONObject
from .math import ( from .math import (
Abs, Abs,
ACos, ACos,
@ -97,7 +98,6 @@ __all__ = [
"Coalesce", "Coalesce",
"Collate", "Collate",
"Greatest", "Greatest",
"JSONObject",
"Least", "Least",
"NullIf", "NullIf",
# datetime # datetime
@ -125,6 +125,8 @@ __all__ = [
"TruncTime", "TruncTime",
"TruncWeek", "TruncWeek",
"TruncYear", "TruncYear",
# json
"JSONObject",
# math # math
"Abs", "Abs",
"ACos", "ACos",

View File

@ -1,9 +1,6 @@
"""Database functions that do comparisons or type conversions.""" """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.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 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) 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): class Least(Func):
""" """
Return the minimum expression. Return the minimum expression.

View 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)

View File

@ -163,31 +163,6 @@ and ``comment.modified``.
The PostgreSQL behavior can be emulated using ``Coalesce`` if you know The PostgreSQL behavior can be emulated using ``Coalesce`` if you know
a sensible minimum value to provide as a default. a sensible minimum value to provide as a default.
``JSONObject``
--------------
.. class:: JSONObject(**fields)
Takes a list of key-value pairs and returns a JSON object containing those
pairs.
Usage example:
.. code-block:: pycon
>>> from django.db.models import F
>>> from django.db.models.functions import JSONObject, Lower
>>> Author.objects.create(name="Margaret Smith", alias="msmith", age=25)
>>> author = Author.objects.annotate(
... json_object=JSONObject(
... name=Lower("name"),
... alias="alias",
... age=F("age") * 2,
... )
... ).get()
>>> author.json_object
{'name': 'margaret smith', 'alias': 'msmith', 'age': 50}
``Least`` ``Least``
--------- ---------
@ -861,6 +836,36 @@ that deal with time-parts can be used with ``TimeField``:
2014-06-16 00:00:00+10:00 2 2014-06-16 00:00:00+10:00 2
2016-01-01 04:00:00+11:00 1 2016-01-01 04:00:00+11:00 1
.. _json-functions:
JSON Functions
==============
``JSONObject``
--------------
.. class:: JSONObject(**fields)
Takes a list of key-value pairs and returns a JSON object containing those
pairs.
Usage example:
.. code-block:: pycon
>>> from django.db.models import F
>>> from django.db.models.functions import JSONObject, Lower
>>> Author.objects.create(name="Margaret Smith", alias="msmith", age=25)
>>> author = Author.objects.annotate(
... json_object=JSONObject(
... name=Lower("name"),
... alias="alias",
... age=F("age") * 2,
... )
... ).get()
>>> author.json_object
{'name': 'margaret smith', 'alias': 'msmith', 'age': 50}
.. _math-functions: .. _math-functions:
Math Functions Math Functions

View File