diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py index 7424f46e660..16231f24ace 100644 --- a/django/db/models/fields/json.py +++ b/django/db/models/fields/json.py @@ -4,6 +4,7 @@ from django import forms from django.core import checks, exceptions from django.db import NotSupportedError, connections, router from django.db.models import lookups +from django.db.models.fields import TextField from django.db.models.lookups import PostgresOperatorLookup, Transform from django.utils.translation import gettext_lazy as _ @@ -366,6 +367,17 @@ class KeyTransform(Transform): class KeyTextTransform(KeyTransform): postgres_operator = "->>" postgres_nested_operator = "#>>" + output_field = TextField() + + def as_mysql(self, compiler, connection): + if connection.mysql_is_mariadb: + # MariaDB doesn't support -> and ->> operators (see MDEV-13594). + sql, params = super().as_mysql(compiler, connection) + return "JSON_UNQUOTE(%s)" % sql, params + else: + lhs, params, key_transforms = self.preprocess_lhs(compiler, connection) + json_path = compile_json_path(key_transforms) + return "(%s ->> %%s)" % lhs, tuple(params) + (json_path,) class KeyTransformTextLookupMixin: diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 25dfef7a244..d6b9f031b13 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -370,6 +370,7 @@ class TestQuerying(TestCase): ).order_by("key"), ): self.assertSequenceEqual(qs, [self.objs[4]]) + none_val = "" if connection.features.interprets_empty_strings_as_nulls else None qs = NullableJSONModel.objects.filter(value__isnull=False) self.assertQuerysetEqual( qs.filter(value__isnull=False) @@ -381,7 +382,7 @@ class TestQuerying(TestCase): .values("key") .annotate(count=Count("key")) .order_by("count"), - [(None, 0), ("g", 1)], + [(none_val, 0), ("g", 1)], operator.itemgetter("key", "count"), ) @@ -494,6 +495,17 @@ class TestQuerying(TestCase): [self.objs[4]], ) + def test_key_text_transform_char_lookup(self): + qs = NullableJSONModel.objects.annotate( + char_value=KeyTextTransform("foo", "value"), + ).filter(char_value__startswith="bar") + self.assertSequenceEqual(qs, [self.objs[7]]) + + qs = NullableJSONModel.objects.annotate( + char_value=KeyTextTransform(1, KeyTextTransform("bar", "value")), + ).filter(char_value__startswith="bar") + self.assertSequenceEqual(qs, [self.objs[7]]) + def test_expression_wrapper_key_transform(self): self.assertCountEqual( NullableJSONModel.objects.annotate(