Opened 5 months ago
Closed 5 months ago
#36445 closed Bug (wontfix)
Value(None, output_field=JSONField()) incorrectly saves as SQL NULL in bulk_update()
| Reported by: | Clifford Gama | Owned by: | Uddyan Goyal |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | Simon Charette, Thomas | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | yes |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When using bulk_update() with a Value(None, output_field=JSONField()) expression, the value is incorrectly saved as a SQL NULL rather than a JSON null. This behavior was temporarily fixed in 00c690efbc0b10f67924687f24a7b30397bf47d9 due to incomplete for_save propagation during expression resolution (bug in #36419), but reappears after the proper restoration of for_save handling in #36419.
-
tests/queries/test_bulk_update.py
diff --git a/tests/queries/test_bulk_update.py b/tests/queries/test_bulk_update.py index 480fac6784..6c553421cb 100644
a b from math import ceil 3 3 4 4 from django.core.exceptions import FieldDoesNotExist 5 5 from django.db import connection 6 from django.db.models import F 6 from django.db.models import F, Value, JSONField 7 7 from django.db.models.functions import Coalesce, Lower 8 8 from django.db.utils import IntegrityError 9 9 from django.test import TestCase, override_settings, skipUnlessDBFeature … … class BulkUpdateTests(TestCase): 315 315 sql_null_qs = JSONFieldNullable.objects.filter(json_field__isnull=True) 316 316 self.assertSequenceEqual(sql_null_qs, [obj]) 317 317 318 @skipUnlessDBFeature("supports_json_field") 319 def test_json_field_json_null_value(self): 320 obj = JSONFieldNullable.objects.create(json_field={}) 321 obj.json_field = Value(None, output_field=JSONField()) 322 JSONFieldNullable.objects.bulk_update([obj], fields=["json_field"]) 323 obj.refresh_from_db() 324 json_null_qs = JSONFieldNullable.objects.filter(json_field=None) 325 self.assertSequenceEqual(json_null_qs, [obj]) 326 318 327 def test_nullable_fk_after_related_save(self): 319 328 parent = RelatedObject.objects.create() 320 329 child = SingleObject()
Change History (13)
comment:1 by , 5 months ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:2 by , 5 months ago
| Description: | modified (diff) |
|---|
comment:3 by , 5 months ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
follow-up: 11 comment:4 by , 5 months ago
Now that we know this isn't a regression should we consider biting the bullet and stop trying to make Value(None, JSONField()) work by introducing a proper JSONNull expression as described in ticket:35381#comment:3?
comment:5 by , 5 months ago
| Has patch: | set |
|---|
Hello this ticket was unassigned so I assigned this to myself as mentioned in the documentation. I have raised a [PR https://github.com/django/django/pull/19546] for this bug. Please review. I have tried to follow all the instructions as mentioned in the documentation. Please let me know if I missed anything.
Thanks!
comment:6 by , 5 months ago
@Simon Charette: It may not be a regression for the bulk_update case outlined here (of which I don't fully understand the chronology), but isn't it a regression in the case I outlined in #36453 (which has been marked as a duplicate of this issue)? Code that has been working a certain way for 1-2 years is broken in 5.2.3.
comment:7 by , 5 months ago
| Cc: | added |
|---|
Now that we know this isn't a regression
I wasn't involved in the regression status determination, my comment was referring to this discussion and assuming it was the case. It is possible that the issue you reported in #36453 is a valid regression in Django 5.2.3 for a slightly different scenario as When / Case have a variation of expression resolving than Coalesce.
If you can write a regression test that passes for < 5.2 and fail for 5.2.3 then it is effectively a regression and closing #36453, and potentially categorizing this ticket as an issue predating 5.2, was likely a triage mistake.
comment:8 by , 5 months ago
I've made a regression test for the use case detailed in #36453.
https://github.com/thomas-mckay/django/commit/5ca2b19568b39c41248479ad4b19973fe622c438
After bisection, I've noticed that the test passes from 4.2 to 5.2.2. It breaks in 5.2.3 and before 4.2.
To recap:
- https://github.com/django/django/commit/5c23d9f0c32f166c81ecb6f3f01d5077a6084318 was introduced in
4.2. Before this commit the test fails, after this commit the test passes, until5.2.3 - https://github.com/django/django/commit/6fc620b4a8e91839b93af2b52d80bdbd5f8a1fcc was introduced in
5.2.3. After this commit the test fails again.
I've checked Value.for_save, and it is indeed False in 5.2.2 and True in 5.2.3 for the Value in the When condition. So, I understand why #36453 may have been closed as a duplicate of this one: it sounds like the same change may be responsible. My only concern is that the bug in this ticket only covers one of the side-effects of that change (that may or may not be a regression), but not the one I'm experiencing (which I really hope is considered a regression), and that addressing this side-effect will not fix the behavior that's been broken in my case. Let me know if I'm taking this ticket off-topic and I'll re-open my ticket and continue there.
comment:9 by , 5 months ago
I've tested the patch proposed by @GoyalRocks007 in comment:5. With this patch the test passes again.
comment:10 by , 5 months ago
| Cc: | added |
|---|
comment:11 by , 5 months ago
Replying to Simon Charette:
Now that we know this isn't a regression should we consider biting the bullet and stop trying to make
Value(None, JSONField())work by introducing a properJSONNullexpression as described in ticket:35381#comment:3?
In light of recent reports around JSON null/SQL NULL, (the latest being #36453) I'd say yes. Would this mean marking this ticket as invalid in favour of a #35381 repurposed for the JSONNull new feature?
comment:12 by , 5 months ago
| Patch needs improvement: | set |
|---|
I see that there is a growing consensus to pursue JSONNull (ref ticket:36445#comment:4, ticket:35381#comment:10, ticket:36445#comment:11).
We should pursue this before bolting on more logic to trying to resolve the json null problem (note that I confirmed that this test in the PR has been failing since at least Django 4.1 and potentially has never worked)
comment:13 by , 5 months ago
| Resolution: | → wontfix |
|---|---|
| Status: | assigned → closed |
#35381 was repurposed so this ticket is being closed as wontfix following previous conversations.
Thank you for the ticket