mirror of
https://github.com/django/django.git
synced 2025-11-25 21:22:14 +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