summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/models/expressions.py2
-rw-r--r--docs/releases/5.2.3.txt4
-rw-r--r--tests/queries/test_bulk_update.py17
3 files changed, 21 insertions, 2 deletions
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 444e2fab7b..49a29392da 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -297,7 +297,7 @@ class BaseExpression:
c.is_summary = summarize
source_expressions = [
(
- expr.resolve_expression(query, allow_joins, reuse, summarize)
+ expr.resolve_expression(query, allow_joins, reuse, summarize, for_save)
if expr is not None
else None
)
diff --git a/docs/releases/5.2.3.txt b/docs/releases/5.2.3.txt
index 5aaa7fd2dd..927ec16d34 100644
--- a/docs/releases/5.2.3.txt
+++ b/docs/releases/5.2.3.txt
@@ -13,3 +13,7 @@ Bugfixes
* Fixed a log injection possibility by migrating remaining response logging
to ``django.utils.log.log_response()``, which safely escapes arguments such
as the request path to prevent unsafe log output (:cve:`2025-48432`).
+
+* Fixed a regression in Django 5.2 that caused :meth:`.QuerySet.bulk_update` to
+ incorrectly convert ``None`` to JSON ``null`` instead of SQL ``NULL`` for
+ ``JSONField`` (:ticket:`36419`).
diff --git a/tests/queries/test_bulk_update.py b/tests/queries/test_bulk_update.py
index 9fa9c3b9b8..aa2d185800 100644
--- a/tests/queries/test_bulk_update.py
+++ b/tests/queries/test_bulk_update.py
@@ -2,7 +2,7 @@ import datetime
from django.core.exceptions import FieldDoesNotExist
from django.db.models import F
-from django.db.models.functions import Lower
+from django.db.models.functions import Coalesce, Lower
from django.db.utils import IntegrityError
from django.test import TestCase, override_settings, skipUnlessDBFeature
@@ -289,6 +289,21 @@ class BulkUpdateTests(TestCase):
JSONFieldNullable.objects.filter(json_field__has_key="c"), objs
)
+ @skipUnlessDBFeature("supports_json_field")
+ def test_json_field_sql_null(self):
+ obj = JSONFieldNullable.objects.create(json_field={})
+ test_cases = [
+ ("direct_none_assignment", None),
+ ("expression_none_assignment", Coalesce(None, None)),
+ ]
+ for label, value in test_cases:
+ with self.subTest(case=label):
+ obj.json_field = value
+ JSONFieldNullable.objects.bulk_update([obj], fields=["json_field"])
+ obj.refresh_from_db()
+ sql_null_qs = JSONFieldNullable.objects.filter(json_field__isnull=True)
+ self.assertSequenceEqual(sql_null_qs, [obj])
+
def test_nullable_fk_after_related_save(self):
parent = RelatedObject.objects.create()
child = SingleObject()