Skip to content

Commit

Permalink
Fixed #32411 -- Fixed __icontains lookup for JSONField on MySQL.
Browse files Browse the repository at this point in the history
  • Loading branch information
hramezani authored and felixxm committed Feb 5, 2021
1 parent 5163722 commit 63d239d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 20 deletions.
45 changes: 25 additions & 20 deletions django/db/models/fields/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,26 @@ class HasAnyKeys(HasKeys):
logical_operator = ' OR '


class CaseInsensitiveMixin:
"""
Mixin to allow case-insensitive comparison of JSON values on MySQL.
MySQL handles strings used in JSON context using the utf8mb4_bin collation.
Because utf8mb4_bin is a binary collation, comparison of JSON values is
case-sensitive.
"""
def process_lhs(self, compiler, connection):
lhs, lhs_params = super().process_lhs(compiler, connection)
if connection.vendor == 'mysql':
return 'LOWER(%s)' % lhs, lhs_params
return lhs, lhs_params

def process_rhs(self, compiler, connection):
rhs, rhs_params = super().process_rhs(compiler, connection)
if connection.vendor == 'mysql':
return 'LOWER(%s)' % rhs, rhs_params
return rhs, rhs_params


class JSONExact(lookups.Exact):
can_use_none_as_rhs = True

Expand All @@ -260,12 +280,17 @@ def process_rhs(self, compiler, connection):
return rhs, rhs_params


class JSONIContains(CaseInsensitiveMixin, lookups.IContains):
pass


JSONField.register_lookup(DataContains)
JSONField.register_lookup(ContainedBy)
JSONField.register_lookup(HasKey)
JSONField.register_lookup(HasKeys)
JSONField.register_lookup(HasAnyKeys)
JSONField.register_lookup(JSONExact)
JSONField.register_lookup(JSONIContains)


class KeyTransform(Transform):
Expand Down Expand Up @@ -343,26 +368,6 @@ def __init__(self, key_transform, *args, **kwargs):
super().__init__(key_text_transform, *args, **kwargs)


class CaseInsensitiveMixin:
"""
Mixin to allow case-insensitive comparison of JSON values on MySQL.
MySQL handles strings used in JSON context using the utf8mb4_bin collation.
Because utf8mb4_bin is a binary collation, comparison of JSON values is
case-sensitive.
"""
def process_lhs(self, compiler, connection):
lhs, lhs_params = super().process_lhs(compiler, connection)
if connection.vendor == 'mysql':
return 'LOWER(%s)' % lhs, lhs_params
return lhs, lhs_params

def process_rhs(self, compiler, connection):
rhs, rhs_params = super().process_rhs(compiler, connection)
if connection.vendor == 'mysql':
return 'LOWER(%s)' % rhs, rhs_params
return rhs, rhs_params


class KeyTransformIsNull(lookups.IsNull):
# key__isnull=False is the same as has_key='key'
def as_oracle(self, compiler, connection):
Expand Down
6 changes: 6 additions & 0 deletions tests/model_fields/test_jsonfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,12 @@ def test_exact_complex(self):
[self.objs[3]],
)

def test_icontains(self):
self.assertSequenceEqual(
NullableJSONModel.objects.filter(value__icontains='BaX'),
self.objs[6:8],
)

def test_isnull(self):
self.assertSequenceEqual(
NullableJSONModel.objects.filter(value__isnull=True),
Expand Down

0 comments on commit 63d239d

Please sign in to comment.