Skip to content

Commit 19f0ada

Browse files
authored
Prevent JSONField from adding CAST(... AS JSON) for str/int/float (#621)
Fixes #480.
1 parent d0e3549 commit 19f0ada

File tree

4 files changed

+38
-8
lines changed

4 files changed

+38
-8
lines changed

HISTORY.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Pending
88

99
* Prevent ``collections.abc.Sequence`` warning.
1010
* Drop Django 1.11 support. Only Django 2.0+ is supported now.
11+
* Prevent ``JSONField`` from adding ``CAST(... AS JSON)`` to queries against
12+
``str``, ``int``, and ``float`` types.
1113

1214
3.3.0 (2019-12-10)
1315
------------------

src/django_mysql/models/lookups.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from collections.abc import Sequence
33

44
import django
5-
from django.db.models import CharField, Lookup, Transform
5+
from django.db.models import CharField, Lookup, Transform, Value
66
from django.db.models.lookups import (
77
BuiltinLookup,
88
Exact,
@@ -53,15 +53,21 @@ class JSONLookupMixin:
5353
def get_prep_lookup(self):
5454
value = self.rhs
5555
if not hasattr(value, "resolve_expression") and value is not None:
56-
return JSONValue(value)
56+
if isinstance(value, (str, int, float)) and not isinstance(value, bool):
57+
return Value(value)
58+
else:
59+
return JSONValue(value)
5760
return super().get_prep_lookup()
5861

5962
else:
6063

6164
def get_prep_lookup(self):
6265
value = self.rhs
6366
if not hasattr(value, "_prepare") and value is not None:
64-
return JSONValue(value)
67+
if isinstance(value, (str, int, float)) and not isinstance(value, bool):
68+
return Value(value)
69+
else:
70+
return JSONValue(value)
6571
return super().get_prep_lookup()
6672

6773

tests/testapp/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ class JSONModel(Model):
280280
) and connection._nodb_connection.mysql_version >= (5, 7):
281281
attrs = JSONField(null=True)
282282

283+
name = CharField(max_length=3)
284+
283285
def __unicode__(self):
284286
return str(json.dumps(self.attrs))
285287

tests/testapp/test_jsonfield.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,27 +132,38 @@ def setUp(self):
132132
super().setUp()
133133
JSONModel.objects.bulk_create(
134134
[
135-
JSONModel(attrs={"a": "b"}),
136-
JSONModel(attrs=1337),
135+
JSONModel(attrs={"a": "b"}, name="b"),
136+
JSONModel(attrs=1337, name="x"),
137137
JSONModel(attrs=["an", "array"]),
138138
JSONModel(attrs=None),
139-
JSONModel(attrs="foo"),
139+
JSONModel(attrs="foo", name="foo"),
140+
JSONModel(attrs=True),
141+
JSONModel(attrs=False),
142+
JSONModel(attrs=0.5),
140143
]
141144
)
142145
self.objs = list(JSONModel.objects.all().order_by("id"))
143146

144147
def test_equal(self):
145148
assert list(JSONModel.objects.filter(attrs={"a": "b"})) == [self.objs[0]]
146149

147-
def test_equal_value(self):
150+
def test_equal_int(self):
148151
assert list(JSONModel.objects.filter(attrs=1337)) == [self.objs[1]]
149152

153+
def test_equal_float(self):
154+
assert list(JSONModel.objects.filter(attrs=0.5)) == [self.objs[7]]
155+
assert list(JSONModel.objects.filter(attrs=0.501)) == []
156+
150157
def test_equal_string(self):
151158
assert list(JSONModel.objects.filter(attrs="foo")) == [self.objs[4]]
152159

153160
def test_equal_array(self):
154161
assert list(JSONModel.objects.filter(attrs=["an", "array"])) == [self.objs[2]]
155162

163+
def test_equal_bool(self):
164+
assert list(JSONModel.objects.filter(attrs=True)) == [self.objs[5]]
165+
assert list(JSONModel.objects.filter(attrs=False)) == [self.objs[6]]
166+
156167
def test_equal_no_match(self):
157168
assert list(JSONModel.objects.filter(attrs={"c": "z"})) == []
158169

@@ -162,7 +173,12 @@ def test_equal_F_attrs(self):
162173
self.objs[1],
163174
self.objs[2],
164175
self.objs[4],
176+
self.objs[5],
177+
self.objs[6],
178+
self.objs[7],
165179
]
180+
assert list(JSONModel.objects.filter(attrs=F("name"))) == [self.objs[4]]
181+
assert list(JSONModel.objects.filter(attrs__a=F("name"))) == [self.objs[0]]
166182

167183
def test_isnull_True(self):
168184
assert list(JSONModel.objects.filter(attrs__isnull=True)) == [self.objs[3]]
@@ -173,6 +189,9 @@ def test_isnull_False(self):
173189
self.objs[1],
174190
self.objs[2],
175191
self.objs[4],
192+
self.objs[5],
193+
self.objs[6],
194+
self.objs[7],
176195
]
177196

178197
def test_range_broken(self):
@@ -507,7 +526,8 @@ class TestSerialization(JSONFieldTestCase):
507526
test_data = """[
508527
{
509528
"fields": {
510-
"attrs": {"a": "b", "c": null}
529+
"attrs": {"a": "b", "c": null},
530+
"name": ""
511531
},
512532
"model": "testapp.jsonmodel",
513533
"pk": null

0 commit comments

Comments
 (0)