1
0
mirror of https://github.com/django/django.git synced 2024-12-22 09:05:43 +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 (
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",

View File

@ -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.

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
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``
---------
@ -861,6 +836,36 @@ that deal with time-parts can be used with ``TimeField``:
2014-06-16 00:00:00+10:00 2
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

View File