diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py index 29c6ff09260..1b0601018ed 100644 --- a/django/db/models/fields/json.py +++ b/django/db/models/fields/json.py @@ -366,14 +366,25 @@ class CaseInsensitiveMixin: class KeyTransformIsNull(lookups.IsNull): # key__isnull=False is the same as has_key='key' def as_oracle(self, compiler, connection): + sql, params = HasKey( + self.lhs.lhs, + self.lhs.key_name, + ).as_oracle(compiler, connection) if not self.rhs: - return HasKey(self.lhs.lhs, self.lhs.key_name).as_oracle(compiler, connection) - return super().as_sql(compiler, connection) + return sql, params + # Column doesn't have a key or IS NULL. + lhs, lhs_params, _ = self.lhs.preprocess_lhs(compiler, connection) + return '(NOT %s OR %s IS NULL)' % (sql, lhs), tuple(params) + tuple(lhs_params) def as_sqlite(self, compiler, connection): + template = 'JSON_TYPE(%s, %%s) IS NULL' if not self.rhs: - return HasKey(self.lhs.lhs, self.lhs.key_name).as_sqlite(compiler, connection) - return super().as_sql(compiler, connection) + template = 'JSON_TYPE(%s, %%s) IS NOT NULL' + return HasKey(self.lhs.lhs, self.lhs.key_name).as_sql( + compiler, + connection, + template=template, + ) class KeyTransformIn(lookups.In): diff --git a/docs/releases/3.1.5.txt b/docs/releases/3.1.5.txt index 4a7fcc58764..0d111184461 100644 --- a/docs/releases/3.1.5.txt +++ b/docs/releases/3.1.5.txt @@ -9,4 +9,6 @@ Django 3.1.5 fixes several bugs in 3.1.4. Bugfixes ======== -* ... +* Fixed ``__isnull=True`` lookup on key transforms for + :class:`~django.db.models.JSONField` with Oracle and SQLite + (:ticket:`32252`). diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index f71efcff616..c6b2f85e1ef 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -586,6 +586,10 @@ class TestQuerying(TestCase): NullableJSONModel.objects.filter(value__a__isnull=True), self.objs[:3] + self.objs[5:], ) + self.assertSequenceEqual( + NullableJSONModel.objects.filter(value__j__isnull=True), + self.objs[:4] + self.objs[5:], + ) self.assertSequenceEqual( NullableJSONModel.objects.filter(value__a__isnull=False), [self.objs[3], self.objs[4]],