1
0
mirror of https://github.com/django/django.git synced 2025-03-06 07:22:32 +00:00

[5.2.x] Fixed #35167 -- Delegated to super() in JSONField.get_db_prep_save().

Avoids reports of bulk_update() sending Cast expressions
to JSONField.get_prep_value().

Co-authored-by: Simon Charette <charette.s@gmail.com>

Backport of 0bf412111be686b6b23e00863f5d449d63557dbf from main.
This commit is contained in:
Jacob Walls 2025-02-16 21:35:12 -05:00 committed by Sarah Boyce
parent bb4f65ec87
commit 9525135698
3 changed files with 41 additions and 8 deletions

View File

@ -99,18 +99,23 @@ class JSONField(CheckFieldDefaultMixin, Field):
def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
if isinstance(value, expressions.Value) and isinstance(
value.output_field, JSONField
):
value = value.value
elif hasattr(value, "as_sql"):
return value
return connection.ops.adapt_json_value(value, self.encoder)
def get_db_prep_save(self, value, connection):
# This slightly involved logic is to allow for `None` to be used to
# store SQL `NULL` while `Value(None, JSONField())` can be used to
# store JSON `null` while preventing compilable `as_sql` values from
# making their way to `get_db_prep_value`, which is what the `super()`
# implementation does.
if value is None:
return value
return self.get_db_prep_value(value, connection)
if (
isinstance(value, expressions.Value)
and value.value is None
and isinstance(value.output_field, JSONField)
):
value = None
return super().get_db_prep_save(value, connection)
def get_transform(self, name):
transform = super().get_transform(name)

View File

@ -430,6 +430,17 @@ class RelatedJSONModel(models.Model):
required_db_features = {"supports_json_field"}
class CustomSerializationJSONModel(models.Model):
class StringifiedJSONField(models.JSONField):
def get_prep_value(self, value):
return json.dumps(value, cls=self.encoder)
json_field = StringifiedJSONField()
class Meta:
required_db_features = {"supports_json_field"}
class AllFieldsModel(models.Model):
big_integer = models.BigIntegerField()
binary = models.BinaryField()

View File

@ -40,7 +40,13 @@ from django.db.models.functions import Cast
from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.test.utils import CaptureQueriesContext
from .models import CustomJSONDecoder, JSONModel, NullableJSONModel, RelatedJSONModel
from .models import (
CustomJSONDecoder,
CustomSerializationJSONModel,
JSONModel,
NullableJSONModel,
RelatedJSONModel,
)
@skipUnlessDBFeature("supports_json_field")
@ -298,6 +304,17 @@ class TestSaveLoad(TestCase):
obj.refresh_from_db()
self.assertEqual(obj.value, value)
def test_bulk_update_custom_get_prep_value(self):
objs = CustomSerializationJSONModel.objects.bulk_create(
[CustomSerializationJSONModel(pk=1, json_field={"version": "1"})]
)
objs[0].json_field["version"] = "1-alpha"
CustomSerializationJSONModel.objects.bulk_update(objs, ["json_field"])
self.assertSequenceEqual(
CustomSerializationJSONModel.objects.values("json_field"),
[{"json_field": '{"version": "1-alpha"}'}],
)
@skipUnlessDBFeature("supports_json_field")
class TestQuerying(TestCase):