diff --git a/django/contrib/postgres/lookups.py b/django/contrib/postgres/lookups.py index f0a523d849e..cc5bc022c68 100644 --- a/django/contrib/postgres/lookups.py +++ b/django/contrib/postgres/lookups.py @@ -8,7 +8,7 @@ class PostgresSimpleLookup(FieldGetDbPrepValueMixin, Lookup): def as_sql(self, qn, connection): lhs, lhs_params = self.process_lhs(qn, connection) rhs, rhs_params = self.process_rhs(qn, connection) - params = lhs_params + rhs_params + params = tuple(lhs_params) + tuple(rhs_params) return '%s %s %s' % (lhs, self.operator, rhs), params diff --git a/docs/releases/1.11.26.txt b/docs/releases/1.11.26.txt index a0c39b41687..1a54b47e1b7 100644 --- a/docs/releases/1.11.26.txt +++ b/docs/releases/1.11.26.txt @@ -9,4 +9,7 @@ Django 1.11.26 fixes a regression in 1.11.25. Bugfixes ======== -* ... +* Fixed a crash when using a ``contains``, ``contained_by``, ``has_key``, + ``has_keys``, or ``has_any_keys`` lookup on + :class:`~django.contrib.postgres.fields.JSONField`, if the right or left hand + side of an expression is a key transform (:ticket:`30826`). diff --git a/docs/releases/2.1.14.txt b/docs/releases/2.1.14.txt index 17b001e4e5e..92354462a06 100644 --- a/docs/releases/2.1.14.txt +++ b/docs/releases/2.1.14.txt @@ -9,4 +9,7 @@ Django 2.1.14 fixes a regression in 2.1.13. Bugfixes ======== -* ... +* Fixed a crash when using a ``contains``, ``contained_by``, ``has_key``, + ``has_keys``, or ``has_any_keys`` lookup on + :class:`~django.contrib.postgres.fields.JSONField`, if the right or left hand + side of an expression is a key transform (:ticket:`30826`). diff --git a/docs/releases/2.2.7.txt b/docs/releases/2.2.7.txt index 001af881760..f39587e43eb 100644 --- a/docs/releases/2.2.7.txt +++ b/docs/releases/2.2.7.txt @@ -9,4 +9,7 @@ Django 2.2.7 fixes several bugs in 2.2.6. Bugfixes ======== -* ... +* Fixed a crash when using a ``contains``, ``contained_by``, ``has_key``, + ``has_keys``, or ``has_any_keys`` lookup on + :class:`~django.contrib.postgres.fields.JSONField`, if the right or left hand + side of an expression is a key transform (:ticket:`30826`). diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py index 39cba8f695d..956304d6f5e 100644 --- a/tests/postgres_tests/test_json.py +++ b/tests/postgres_tests/test_json.py @@ -135,7 +135,12 @@ class TestQuerying(PostgreSQLTestCase): 'k': True, 'l': False, }), - JSONModel(field={'foo': 'bar'}), + JSONModel(field={ + 'foo': 'bar', + 'baz': {'a': 'b', 'c': 'd'}, + 'bar': ['foo', 'bar'], + 'bax': {'foo': 'bar'}, + }), ]) def test_exact(self): @@ -386,6 +391,26 @@ class TestQuerying(PostgreSQLTestCase): queries[0]['sql'], ) + def test_lookups_with_key_transform(self): + tests = ( + ('field__d__contains', 'e'), + ('field__baz__contained_by', {'a': 'b', 'c': 'd', 'e': 'f'}), + ('field__baz__has_key', 'c'), + ('field__baz__has_keys', ['a', 'c']), + ('field__baz__has_any_keys', ['a', 'x']), + ('field__contains', KeyTransform('bax', 'field')), + ( + 'field__contained_by', + KeyTransform('x', RawSQL('%s::jsonb', ['{"x": {"a": "b", "c": 1, "d": "e"}}'])), + ), + ('field__has_key', KeyTextTransform('foo', 'field')), + ) + for lookup, value in tests: + with self.subTest(lookup=lookup): + self.assertTrue(JSONModel.objects.filter( + **{lookup: value}, + ).exists()) + @isolate_apps('postgres_tests') class TestChecks(PostgreSQLSimpleTestCase):