mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	This commit is contained in:
		
				
					committed by
					
						 Sarah Boyce
						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) | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										0
									
								
								tests/db_functions/json/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/db_functions/json/__init__.py
									
									
									
									
									
										Normal file
									
								
							
		Reference in New Issue
	
	Block a user