Skip to content

Commit 3434dbd

Browse files
charettesfelixxm
authored andcommitted
Fixed #34754 -- Fixed JSONField check constraints validation on NULL values.
The __isnull lookup of JSONField must special case Value(None, JSONField()) left-hand-side in order to be coherent with its convoluted null handling. Since psycopg>=3 offers no way to pass a NULL::jsonb the issue is resolved by optimizing IsNull(Value(None), True | False) to True | False. Regression in 5c23d9f. Thanks Alexandre Collet for the report.
1 parent 2b58238 commit 3434dbd

File tree

4 files changed

+35
-1
lines changed

4 files changed

+35
-1
lines changed

django/db/models/lookups.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,11 @@ def as_sql(self, compiler, connection):
607607
raise ValueError(
608608
"The QuerySet value for an isnull lookup must be True or False."
609609
)
610+
if isinstance(self.lhs, Value) and self.lhs.value is None:
611+
if self.rhs:
612+
raise FullResultSet
613+
else:
614+
raise EmptyResultSet
610615
sql, params = self.process_lhs(compiler, connection)
611616
if self.rhs:
612617
return "%s IS NULL" % sql, params

docs/releases/4.2.5.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ Django 4.2.5 fixes several bugs in 4.2.4.
99
Bugfixes
1010
========
1111

12-
* ...
12+
* Fixed a regression in Django 4.2 that caused an incorrect validation of
13+
``CheckConstraints`` on ``__isnull`` lookups against ``JSONField``
14+
(:ticket:`34754`).

tests/constraints/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,10 @@ class Meta:
121121

122122
class ChildModel(AbstractModel):
123123
pass
124+
125+
126+
class JSONFieldModel(models.Model):
127+
data = models.JSONField(null=True)
128+
129+
class Meta:
130+
required_db_features = {"supports_json_field"}

tests/constraints/tests.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .models import (
1414
ChildModel,
1515
ChildUniqueConstraintProduct,
16+
JSONFieldModel,
1617
Product,
1718
UniqueConstraintConditionProduct,
1819
UniqueConstraintDeferrable,
@@ -332,6 +333,25 @@ def test_validate_nullable_field_with_isnull(self):
332333
)
333334
constraint.validate(Product, Product())
334335

336+
@skipUnlessDBFeature("supports_json_field")
337+
def test_validate_nullable_jsonfield(self):
338+
is_null_constraint = models.CheckConstraint(
339+
check=models.Q(data__isnull=True),
340+
name="nullable_data",
341+
)
342+
is_not_null_constraint = models.CheckConstraint(
343+
check=models.Q(data__isnull=False),
344+
name="nullable_data",
345+
)
346+
is_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None))
347+
msg = f"Constraint “{is_null_constraint.name}” is violated."
348+
with self.assertRaisesMessage(ValidationError, msg):
349+
is_null_constraint.validate(JSONFieldModel, JSONFieldModel(data={}))
350+
msg = f"Constraint “{is_not_null_constraint.name}” is violated."
351+
with self.assertRaisesMessage(ValidationError, msg):
352+
is_not_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None))
353+
is_not_null_constraint.validate(JSONFieldModel, JSONFieldModel(data={}))
354+
335355

336356
class UniqueConstraintTests(TestCase):
337357
@classmethod

0 commit comments

Comments
 (0)