mirror of
https://github.com/django/django.git
synced 2025-03-13 10:50:55 +00:00
This commit is contained in:
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 (
|
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",
|
||||||
|
@ -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.
|
||||||
|
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)
|
@ -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
|
||||||
|
0
tests/db_functions/json/__init__.py
Normal file
0
tests/db_functions/json/__init__.py
Normal file
Loading…
x
Reference in New Issue
Block a user