mirror of
https://github.com/django/django.git
synced 2025-11-07 07:15:35 +00:00
Refs #35381 -- Delegated ArrayField element prepping to base_field.get_db_prep_save.
Previously, ArrayField always used base_field.get_db_prep_value when saving, which could differ from how base_field prepares data for save. This change overrides ArrayField.get_db_prep_save to delegate to the base_field's get_db_prep_save, ensuring elements like None in JSONField arrays are saved correctly as SQL NULL instead of JSON null.
This commit is contained in:
committed by
Jacob Walls
parent
adc25a9a66
commit
be7f68422d
@@ -135,6 +135,11 @@ class ArrayField(CheckPostgresInstalledMixin, CheckFieldDefaultMixin, Field):
|
|||||||
]
|
]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def get_db_prep_save(self, value, connection):
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
return [self.base_field.get_db_prep_save(i, connection) for i in value]
|
||||||
|
return value
|
||||||
|
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
name, path, args, kwargs = super().deconstruct()
|
name, path, args, kwargs = super().deconstruct()
|
||||||
if path == "django.contrib.postgres.fields.array.ArrayField":
|
if path == "django.contrib.postgres.fields.array.ArrayField":
|
||||||
|
|||||||
@@ -319,6 +319,15 @@ backends.
|
|||||||
|
|
||||||
* Support for PostGIS 3.1 is removed.
|
* Support for PostGIS 3.1 is removed.
|
||||||
|
|
||||||
|
:mod:`django.contrib.postgres`
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* Top-level elements set to ``None`` in an
|
||||||
|
:class:`~django.contrib.postgres.fields.ArrayField` with a
|
||||||
|
:class:`~django.db.models.JSONField` base field are now saved as SQL ``NULL``
|
||||||
|
instead of the JSON ``null`` primitive. This matches the behavior of a
|
||||||
|
standalone :class:`~django.db.models.JSONField` when storing ``None`` values.
|
||||||
|
|
||||||
Dropped support for PostgreSQL 14
|
Dropped support for PostgreSQL 14
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class OtherTypesArrayModel(PostgreSQLModel):
|
|||||||
models.DecimalField(max_digits=5, decimal_places=2), default=list
|
models.DecimalField(max_digits=5, decimal_places=2), default=list
|
||||||
)
|
)
|
||||||
tags = ArrayField(TagField(), blank=True, null=True)
|
tags = ArrayField(TagField(), blank=True, null=True)
|
||||||
json = ArrayField(models.JSONField(default=dict), default=list)
|
json = ArrayField(models.JSONField(default=dict), default=list, null=True)
|
||||||
int_ranges = ArrayField(IntegerRangeField(), blank=True, null=True)
|
int_ranges = ArrayField(IntegerRangeField(), blank=True, null=True)
|
||||||
bigint_ranges = ArrayField(BigIntegerRangeField(), blank=True, null=True)
|
bigint_ranges = ArrayField(BigIntegerRangeField(), blank=True, null=True)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.core import checks, exceptions, serializers, validators
|
|||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.db import IntegrityError, connection, models
|
from django.db import IntegrityError, connection, models
|
||||||
|
from django.db.models import JSONNull
|
||||||
from django.db.models.expressions import Exists, F, OuterRef, RawSQL, Value
|
from django.db.models.expressions import Exists, F, OuterRef, RawSQL, Value
|
||||||
from django.db.models.functions import Cast, JSONObject, Upper
|
from django.db.models.functions import Cast, JSONObject, Upper
|
||||||
from django.test import TransactionTestCase, override_settings, skipUnlessDBFeature
|
from django.test import TransactionTestCase, override_settings, skipUnlessDBFeature
|
||||||
@@ -1577,3 +1578,29 @@ class TestAdminUtils(PostgreSQLTestCase):
|
|||||||
self.empty_value,
|
self.empty_value,
|
||||||
)
|
)
|
||||||
self.assertEqual(display_value, self.empty_value)
|
self.assertEqual(display_value, self.empty_value)
|
||||||
|
|
||||||
|
|
||||||
|
class TestJSONFieldQuerying(PostgreSQLTestCase):
|
||||||
|
def test_saving_and_querying_for_sql_null(self):
|
||||||
|
obj = OtherTypesArrayModel.objects.create(json=[None, None])
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
OtherTypesArrayModel.objects.filter(json__1__isnull=True), [obj]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_saving_and_querying_for_json_null(self):
|
||||||
|
obj = OtherTypesArrayModel.objects.create(json=[JSONNull(), JSONNull()])
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
OtherTypesArrayModel.objects.filter(json__1=JSONNull()), [obj]
|
||||||
|
)
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
OtherTypesArrayModel.objects.filter(json__1__isnull=True), []
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_saving_and_querying_for_nested_json_nulls(self):
|
||||||
|
obj = OtherTypesArrayModel.objects.create(json=[[None, 1], [None, 2]])
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
OtherTypesArrayModel.objects.filter(json__1__0=None), [obj]
|
||||||
|
)
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
OtherTypesArrayModel.objects.filter(json__1__0__isnull=True), []
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user