mirror of
https://github.com/django/django.git
synced 2025-07-07 21:35:15 +00:00
Fixed #36419 -- Ensured for_save was propagated when resolving expressions.
Some checks failed
Docs / docs (push) Has been cancelled
Docs / blacken-docs (push) Has been cancelled
Linters / flake8 (push) Has been cancelled
Linters / isort (push) Has been cancelled
Linters / black (push) Has been cancelled
Tests / Windows, SQLite, Python 3.13 (push) Has been cancelled
Tests / JavaScript tests (push) Has been cancelled
Some checks failed
Docs / docs (push) Has been cancelled
Docs / blacken-docs (push) Has been cancelled
Linters / flake8 (push) Has been cancelled
Linters / isort (push) Has been cancelled
Linters / black (push) Has been cancelled
Tests / Windows, SQLite, Python 3.13 (push) Has been cancelled
Tests / JavaScript tests (push) Has been cancelled
The for_save flag wasn't properly propagated when resolving expressions, which
prevented get_db_prep_save() from being called in some cases. This affected
fields like JSONField where None would be saved as JSON null instead of SQL NULL.
Regression in 00c690efbc
.
Thanks to David Sanders and Simon Charette for reviews.
Co-authored-by: Adam Johnson <me@adamj.eu>
This commit is contained in:
parent
9579517552
commit
c1fa3fdd04
3 changed files with 21 additions and 2 deletions
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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`).
|
||||
|
|
|
@ -4,7 +4,7 @@ from math import ceil
|
|||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.db import connection
|
||||
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
|
||||
|
||||
|
@ -300,6 +300,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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue