test_jsonfield.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. import operator
  2. import uuid
  3. from unittest import mock
  4. from django import forms
  5. from django.core import serializers
  6. from django.core.exceptions import ValidationError
  7. from django.core.serializers.json import DjangoJSONEncoder
  8. from django.db import (
  9. DataError,
  10. IntegrityError,
  11. NotSupportedError,
  12. OperationalError,
  13. connection,
  14. models,
  15. transaction,
  16. )
  17. from django.db.models import (
  18. Count,
  19. ExpressionWrapper,
  20. F,
  21. IntegerField,
  22. JSONField,
  23. OuterRef,
  24. Q,
  25. Subquery,
  26. Transform,
  27. Value,
  28. )
  29. from django.db.models.expressions import RawSQL
  30. from django.db.models.fields.json import (
  31. KT,
  32. HasKey,
  33. KeyTextTransform,
  34. KeyTransform,
  35. KeyTransformFactory,
  36. KeyTransformTextLookupMixin,
  37. )
  38. from django.db.models.functions import Cast
  39. from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature
  40. from django.test.utils import CaptureQueriesContext
  41. from .models import (
  42. CustomJSONDecoder,
  43. CustomSerializationJSONModel,
  44. JSONModel,
  45. NullableJSONModel,
  46. RelatedJSONModel,
  47. )
  48. @skipUnlessDBFeature("supports_json_field")
  49. class JSONFieldTests(TestCase):
  50. def test_invalid_value(self):
  51. msg = "is not JSON serializable"
  52. with self.assertRaisesMessage(TypeError, msg):
  53. NullableJSONModel.objects.create(
  54. value={
  55. "uuid": uuid.UUID("d85e2076-b67c-4ee7-8c3a-2bf5a2cc2475"),
  56. }
  57. )
  58. def test_custom_encoder_decoder(self):
  59. value = {"uuid": uuid.UUID("{d85e2076-b67c-4ee7-8c3a-2bf5a2cc2475}")}
  60. obj = NullableJSONModel(value_custom=value)
  61. obj.clean_fields()
  62. obj.save()
  63. obj.refresh_from_db()
  64. self.assertEqual(obj.value_custom, value)
  65. def test_db_check_constraints(self):
  66. value = "{@!invalid json value 123 $!@#"
  67. with mock.patch.object(DjangoJSONEncoder, "encode", return_value=value):
  68. with self.assertRaises((IntegrityError, DataError, OperationalError)):
  69. NullableJSONModel.objects.create(value_custom=value)
  70. class TestMethods(SimpleTestCase):
  71. def test_deconstruct(self):
  72. field = models.JSONField()
  73. name, path, args, kwargs = field.deconstruct()
  74. self.assertEqual(path, "django.db.models.JSONField")
  75. self.assertEqual(args, [])
  76. self.assertEqual(kwargs, {})
  77. def test_deconstruct_custom_encoder_decoder(self):
  78. field = models.JSONField(encoder=DjangoJSONEncoder, decoder=CustomJSONDecoder)
  79. name, path, args, kwargs = field.deconstruct()
  80. self.assertEqual(kwargs["encoder"], DjangoJSONEncoder)
  81. self.assertEqual(kwargs["decoder"], CustomJSONDecoder)
  82. def test_get_transforms(self):
  83. @models.JSONField.register_lookup
  84. class MyTransform(Transform):
  85. lookup_name = "my_transform"
  86. field = models.JSONField()
  87. transform = field.get_transform("my_transform")
  88. self.assertIs(transform, MyTransform)
  89. models.JSONField._unregister_lookup(MyTransform)
  90. transform = field.get_transform("my_transform")
  91. self.assertIsInstance(transform, KeyTransformFactory)
  92. def test_key_transform_text_lookup_mixin_non_key_transform(self):
  93. transform = Transform("test")
  94. msg = (
  95. "Transform should be an instance of KeyTransform in order to use "
  96. "this lookup."
  97. )
  98. with self.assertRaisesMessage(TypeError, msg):
  99. KeyTransformTextLookupMixin(transform)
  100. def test_get_prep_value(self):
  101. class JSONFieldGetPrepValue(models.JSONField):
  102. def get_prep_value(self, value):
  103. if value is True:
  104. return {"value": True}
  105. return value
  106. def noop_adapt_json_value(value, encoder):
  107. return value
  108. field = JSONFieldGetPrepValue()
  109. with mock.patch.object(
  110. connection.ops, "adapt_json_value", noop_adapt_json_value
  111. ):
  112. self.assertEqual(
  113. field.get_db_prep_value(True, connection, prepared=False),
  114. {"value": True},
  115. )
  116. self.assertIs(
  117. field.get_db_prep_value(True, connection, prepared=True), True
  118. )
  119. self.assertEqual(field.get_db_prep_value(1, connection, prepared=False), 1)
  120. class TestValidation(SimpleTestCase):
  121. def test_invalid_encoder(self):
  122. msg = "The encoder parameter must be a callable object."
  123. with self.assertRaisesMessage(ValueError, msg):
  124. models.JSONField(encoder=DjangoJSONEncoder())
  125. def test_invalid_decoder(self):
  126. msg = "The decoder parameter must be a callable object."
  127. with self.assertRaisesMessage(ValueError, msg):
  128. models.JSONField(decoder=CustomJSONDecoder())
  129. def test_validation_error(self):
  130. field = models.JSONField()
  131. msg = "Value must be valid JSON."
  132. value = uuid.UUID("{d85e2076-b67c-4ee7-8c3a-2bf5a2cc2475}")
  133. with self.assertRaisesMessage(ValidationError, msg):
  134. field.clean({"uuid": value}, None)
  135. def test_custom_encoder(self):
  136. field = models.JSONField(encoder=DjangoJSONEncoder)
  137. value = uuid.UUID("{d85e2076-b67c-4ee7-8c3a-2bf5a2cc2475}")
  138. field.clean({"uuid": value}, None)
  139. class TestFormField(SimpleTestCase):
  140. def test_formfield(self):
  141. model_field = models.JSONField()
  142. form_field = model_field.formfield()
  143. self.assertIsInstance(form_field, forms.JSONField)
  144. def test_formfield_custom_encoder_decoder(self):
  145. model_field = models.JSONField(
  146. encoder=DjangoJSONEncoder, decoder=CustomJSONDecoder
  147. )
  148. form_field = model_field.formfield()
  149. self.assertIs(form_field.encoder, DjangoJSONEncoder)
  150. self.assertIs(form_field.decoder, CustomJSONDecoder)
  151. class TestSerialization(SimpleTestCase):
  152. test_data = (
  153. '[{"fields": {"value": %s}, "model": "model_fields.jsonmodel", "pk": null}]'
  154. )
  155. test_values = (
  156. # (Python value, serialized value),
  157. ({"a": "b", "c": None}, '{"a": "b", "c": null}'),
  158. ("abc", '"abc"'),
  159. ('{"a": "a"}', '"{\\"a\\": \\"a\\"}"'),
  160. )
  161. def test_dumping(self):
  162. for value, serialized in self.test_values:
  163. with self.subTest(value=value):
  164. instance = JSONModel(value=value)
  165. data = serializers.serialize("json", [instance])
  166. self.assertJSONEqual(data, self.test_data % serialized)
  167. def test_loading(self):
  168. for value, serialized in self.test_values:
  169. with self.subTest(value=value):
  170. instance = list(
  171. serializers.deserialize("json", self.test_data % serialized)
  172. )[0].object
  173. self.assertEqual(instance.value, value)
  174. def test_xml_serialization(self):
  175. test_xml_data = (
  176. '<django-objects version="1.0">'
  177. '<object model="model_fields.nullablejsonmodel">'
  178. '<field name="value" type="JSONField">%s'
  179. "</field></object></django-objects>"
  180. )
  181. for value, serialized in self.test_values:
  182. with self.subTest(value=value):
  183. instance = NullableJSONModel(value=value)
  184. data = serializers.serialize("xml", [instance], fields=["value"])
  185. self.assertXMLEqual(data, test_xml_data % serialized)
  186. new_instance = list(serializers.deserialize("xml", data))[0].object
  187. self.assertEqual(new_instance.value, instance.value)
  188. @skipUnlessDBFeature("supports_json_field")
  189. class TestSaveLoad(TestCase):
  190. def test_null(self):
  191. obj = NullableJSONModel(value=None)
  192. obj.save()
  193. obj.refresh_from_db()
  194. self.assertIsNone(obj.value)
  195. @skipUnlessDBFeature("supports_primitives_in_json_field")
  196. def test_json_null_different_from_sql_null(self):
  197. json_null = NullableJSONModel.objects.create(value=Value(None, JSONField()))
  198. NullableJSONModel.objects.update(value=Value(None, JSONField()))
  199. json_null.refresh_from_db()
  200. sql_null = NullableJSONModel.objects.create(value=None)
  201. sql_null.refresh_from_db()
  202. # 'null' is not equal to NULL in the database.
  203. self.assertSequenceEqual(
  204. NullableJSONModel.objects.filter(value=Value(None, JSONField())),
  205. [json_null],
  206. )
  207. self.assertSequenceEqual(
  208. NullableJSONModel.objects.filter(value=None),
  209. [json_null],
  210. )
  211. self.assertSequenceEqual(
  212. NullableJSONModel.objects.filter(value__isnull=True),
  213. [sql_null],
  214. )
  215. # 'null' is equal to NULL in Python (None).
  216. self.assertEqual(json_null.value, sql_null.value)
  217. @skipUnlessDBFeature("supports_primitives_in_json_field")
  218. def test_primitives(self):
  219. values = [
  220. True,
  221. 1,
  222. 1.45,
  223. "String",
  224. "",
  225. ]
  226. for value in values:
  227. with self.subTest(value=value):
  228. obj = JSONModel(value=value)
  229. obj.save()
  230. obj.refresh_from_db()
  231. self.assertEqual(obj.value, value)
  232. def test_dict(self):
  233. values = [
  234. {},
  235. {"name": "John", "age": 20, "height": 180.3},
  236. {"a": True, "b": {"b1": False, "b2": None}},
  237. ]
  238. for value in values:
  239. with self.subTest(value=value):
  240. obj = JSONModel.objects.create(value=value)
  241. obj.refresh_from_db()
  242. self.assertEqual(obj.value, value)
  243. def test_list(self):
  244. values = [
  245. [],
  246. ["John", 20, 180.3],
  247. [True, [False, None]],
  248. ]
  249. for value in values:
  250. with self.subTest(value=value):
  251. obj = JSONModel.objects.create(value=value)
  252. obj.refresh_from_db()
  253. self.assertEqual(obj.value, value)
  254. def test_realistic_object(self):
  255. value = {
  256. "name": "John",
  257. "age": 20,
  258. "pets": [
  259. {"name": "Kit", "type": "cat", "age": 2},
  260. {"name": "Max", "type": "dog", "age": 1},
  261. ],
  262. "courses": [
  263. ["A1", "A2", "A3"],
  264. ["B1", "B2"],
  265. ["C1"],
  266. ],
  267. }
  268. obj = JSONModel.objects.create(value=value)
  269. obj.refresh_from_db()
  270. self.assertEqual(obj.value, value)
  271. def test_bulk_update_custom_get_prep_value(self):
  272. objs = CustomSerializationJSONModel.objects.bulk_create(
  273. [CustomSerializationJSONModel(pk=1, json_field={"version": "1"})]
  274. )
  275. objs[0].json_field["version"] = "1-alpha"
  276. CustomSerializationJSONModel.objects.bulk_update(objs, ["json_field"])
  277. self.assertSequenceEqual(
  278. CustomSerializationJSONModel.objects.values("json_field"),
  279. [{"json_field": '{"version": "1-alpha"}'}],
  280. )
  281. @skipUnlessDBFeature("supports_json_field")
  282. class TestQuerying(TestCase):
  283. @classmethod
  284. def setUpTestData(cls):
  285. cls.primitives = [True, False, "yes", 7, 9.6]
  286. values = [
  287. None,
  288. [],
  289. {},
  290. {"a": "b", "c": 14},
  291. {
  292. "a": "b",
  293. "c": 14,
  294. "d": ["e", {"f": "g"}],
  295. "h": True,
  296. "i": False,
  297. "j": None,
  298. "k": {"l": "m"},
  299. "n": [None, True, False],
  300. "o": '"quoted"',
  301. "p": 4.2,
  302. "r": {"s": True, "t": False},
  303. },
  304. [1, [2]],
  305. {"k": True, "l": False, "foo": "bax"},
  306. {
  307. "foo": "bar",
  308. "baz": {"a": "b", "c": "d"},
  309. "bar": ["foo", "bar"],
  310. "bax": {"foo": "bar"},
  311. },
  312. ]
  313. cls.objs = [NullableJSONModel.objects.create(value=value) for value in values]
  314. if connection.features.supports_primitives_in_json_field:
  315. cls.objs.extend(
  316. [
  317. NullableJSONModel.objects.create(value=value)
  318. for value in cls.primitives
  319. ]
  320. )
  321. cls.raw_sql = "%s::jsonb" if connection.vendor == "postgresql" else "%s"
  322. def test_exact(self):
  323. self.assertSequenceEqual(
  324. NullableJSONModel.objects.filter(value__exact={}),
  325. [self.objs[2]],
  326. )
  327. def test_exact_complex(self):
  328. self.assertSequenceEqual(
  329. NullableJSONModel.objects.filter(value__exact={"a": "b", "c": 14}),
  330. [self.objs[3]],
  331. )
  332. def test_icontains(self):
  333. self.assertCountEqual(
  334. NullableJSONModel.objects.filter(value__icontains="BaX"),
  335. self.objs[6:8],
  336. )
  337. def test_isnull(self):
  338. self.assertSequenceEqual(
  339. NullableJSONModel.objects.filter(value__isnull=True),
  340. [self.objs[0]],
  341. )
  342. def test_ordering_by_transform(self):
  343. mariadb = connection.vendor == "mysql" and connection.mysql_is_mariadb
  344. values = [
  345. {"ord": 93, "name": "bar"},
  346. {"ord": 22.1, "name": "foo"},
  347. {"ord": -1, "name": "baz"},
  348. {"ord": 21.931902, "name": "spam"},
  349. {"ord": -100291029, "name": "eggs"},
  350. ]
  351. for field_name in ["value", "value_custom"]:
  352. with self.subTest(field=field_name):
  353. objs = [
  354. NullableJSONModel.objects.create(**{field_name: value})
  355. for value in values
  356. ]
  357. query = NullableJSONModel.objects.filter(
  358. **{"%s__name__isnull" % field_name: False},
  359. ).order_by("%s__ord" % field_name)
  360. expected = [objs[4], objs[2], objs[3], objs[1], objs[0]]
  361. if mariadb or connection.vendor == "oracle":
  362. # MariaDB and Oracle return JSON values as strings.
  363. expected = [objs[2], objs[4], objs[3], objs[1], objs[0]]
  364. self.assertSequenceEqual(query, expected)
  365. def test_ordering_grouping_by_key_transform(self):
  366. base_qs = NullableJSONModel.objects.filter(value__d__0__isnull=False)
  367. for qs in (
  368. base_qs.order_by("value__d__0"),
  369. base_qs.annotate(
  370. key=KeyTransform("0", KeyTransform("d", "value"))
  371. ).order_by("key"),
  372. ):
  373. self.assertSequenceEqual(qs, [self.objs[4]])
  374. none_val = "" if connection.features.interprets_empty_strings_as_nulls else None
  375. qs = NullableJSONModel.objects.filter(value__isnull=False)
  376. self.assertQuerySetEqual(
  377. qs.filter(value__isnull=False)
  378. .annotate(key=KT("value__d__1__f"))
  379. .values("key")
  380. .annotate(count=Count("key"))
  381. .order_by("count"),
  382. [(none_val, 0), ("g", 1)],
  383. operator.itemgetter("key", "count"),
  384. )
  385. def test_ordering_grouping_by_count(self):
  386. qs = (
  387. NullableJSONModel.objects.filter(
  388. value__isnull=False,
  389. )
  390. .values("value__d__0")
  391. .annotate(count=Count("value__d__0"))
  392. .order_by("count")
  393. )
  394. self.assertQuerySetEqual(qs, [0, 1], operator.itemgetter("count"))
  395. def test_order_grouping_custom_decoder(self):
  396. NullableJSONModel.objects.create(value_custom={"a": "b"})
  397. qs = NullableJSONModel.objects.filter(value_custom__isnull=False)
  398. self.assertSequenceEqual(
  399. qs.values(
  400. "value_custom__a",
  401. )
  402. .annotate(
  403. count=Count("id"),
  404. )
  405. .order_by("value_custom__a"),
  406. [{"value_custom__a": "b", "count": 1}],
  407. )
  408. def test_key_transform_raw_expression(self):
  409. expr = RawSQL(self.raw_sql, ['{"x": "bar"}'])
  410. self.assertSequenceEqual(
  411. NullableJSONModel.objects.filter(value__foo=KeyTransform("x", expr)),
  412. [self.objs[7]],
  413. )
  414. def test_nested_key_transform_raw_expression(self):
  415. expr = RawSQL(self.raw_sql, ['{"x": {"y": "bar"}}'])
  416. self.assertSequenceEqual(
  417. NullableJSONModel.objects.filter(
  418. value__foo=KeyTransform("y", KeyTransform("x", expr))
  419. ),
  420. [self.objs[7]],
  421. )
  422. def test_key_transform_expression(self):
  423. self.assertSequenceEqual(
  424. NullableJSONModel.objects.filter(value__d__0__isnull=False)
  425. .annotate(
  426. key=KeyTransform("d", "value"),
  427. chain=KeyTransform("0", "key"),
  428. expr=KeyTransform("0", Cast("key", models.JSONField())),
  429. )
  430. .filter(chain=F("expr")),
  431. [self.objs[4]],
  432. )
  433. def test_key_transform_annotation_expression(self):
  434. obj = NullableJSONModel.objects.create(value={"d": ["e", "e"]})
  435. self.assertSequenceEqual(
  436. NullableJSONModel.objects.filter(value__d__0__isnull=False)
  437. .annotate(
  438. key=F("value__d"),
  439. chain=F("key__0"),
  440. expr=Cast("key", models.JSONField()),
  441. )
  442. .filter(chain=F("expr__1")),
  443. [obj],
  444. )
  445. def test_nested_key_transform_expression(self):
  446. self.assertSequenceEqual(
  447. NullableJSONModel.objects.filter(value__d__0__isnull=False)
  448. .annotate(
  449. key=KeyTransform("d", "value"),
  450. chain=KeyTransform("f", KeyTransform("1", "key")),
  451. expr=KeyTransform(
  452. "f", KeyTransform("1", Cast("key", models.JSONField()))
  453. ),
  454. )
  455. .filter(chain=F("expr")),
  456. [self.objs[4]],
  457. )
  458. def test_nested_key_transform_annotation_expression(self):
  459. obj = NullableJSONModel.objects.create(
  460. value={"d": ["e", {"f": "g"}, {"f": "g"}]},
  461. )
  462. self.assertSequenceEqual(
  463. NullableJSONModel.objects.filter(value__d__0__isnull=False)
  464. .annotate(
  465. key=F("value__d"),
  466. chain=F("key__1__f"),
  467. expr=Cast("key", models.JSONField()),
  468. )
  469. .filter(chain=F("expr__2__f")),
  470. [obj],
  471. )
  472. def test_nested_key_transform_on_subquery(self):
  473. self.assertSequenceEqual(
  474. NullableJSONModel.objects.filter(value__d__0__isnull=False)
  475. .annotate(
  476. subquery_value=Subquery(
  477. NullableJSONModel.objects.filter(pk=OuterRef("pk")).values("value")
  478. ),
  479. key=KeyTransform("d", "subquery_value"),
  480. chain=KeyTransform("f", KeyTransform("1", "key")),
  481. )
  482. .filter(chain="g"),
  483. [self.objs[4]],
  484. )
  485. def test_key_text_transform_char_lookup(self):
  486. qs = NullableJSONModel.objects.annotate(
  487. char_value=KeyTextTransform("foo", "value"),
  488. ).filter(char_value__startswith="bar")
  489. self.assertSequenceEqual(qs, [self.objs[7]])
  490. qs = NullableJSONModel.objects.annotate(
  491. char_value=KeyTextTransform(1, KeyTextTransform("bar", "value")),
  492. ).filter(char_value__startswith="bar")
  493. self.assertSequenceEqual(qs, [self.objs[7]])
  494. def test_expression_wrapper_key_transform(self):
  495. self.assertCountEqual(
  496. NullableJSONModel.objects.annotate(
  497. expr=ExpressionWrapper(
  498. KeyTransform("c", "value"),
  499. output_field=IntegerField(),
  500. ),
  501. ).filter(expr__isnull=False),
  502. self.objs[3:5],
  503. )
  504. def test_has_key(self):
  505. self.assertCountEqual(
  506. NullableJSONModel.objects.filter(value__has_key="a"),
  507. [self.objs[3], self.objs[4]],
  508. )
  509. def test_has_key_null_value(self):
  510. self.assertSequenceEqual(
  511. NullableJSONModel.objects.filter(value__has_key="j"),
  512. [self.objs[4]],
  513. )
  514. def test_has_key_deep(self):
  515. tests = [
  516. (Q(value__baz__has_key="a"), self.objs[7]),
  517. (
  518. Q(value__has_key=KeyTransform("a", KeyTransform("baz", "value"))),
  519. self.objs[7],
  520. ),
  521. (Q(value__has_key=F("value__baz__a")), self.objs[7]),
  522. (
  523. Q(value__has_key=KeyTransform("c", KeyTransform("baz", "value"))),
  524. self.objs[7],
  525. ),
  526. (Q(value__has_key=F("value__baz__c")), self.objs[7]),
  527. (Q(value__d__1__has_key="f"), self.objs[4]),
  528. (
  529. Q(
  530. value__has_key=KeyTransform(
  531. "f", KeyTransform("1", KeyTransform("d", "value"))
  532. )
  533. ),
  534. self.objs[4],
  535. ),
  536. (Q(value__has_key=F("value__d__1__f")), self.objs[4]),
  537. ]
  538. for condition, expected in tests:
  539. with self.subTest(condition=condition):
  540. self.assertSequenceEqual(
  541. NullableJSONModel.objects.filter(condition),
  542. [expected],
  543. )
  544. def test_has_key_literal_lookup(self):
  545. self.assertSequenceEqual(
  546. NullableJSONModel.objects.filter(
  547. HasKey(Value({"foo": "bar"}, JSONField()), "foo")
  548. ).order_by("id"),
  549. self.objs,
  550. )
  551. def test_has_key_list(self):
  552. obj = NullableJSONModel.objects.create(value=[{"a": 1}, {"b": "x"}])
  553. tests = [
  554. Q(value__1__has_key="b"),
  555. Q(value__has_key=KeyTransform("b", KeyTransform(1, "value"))),
  556. Q(value__has_key=KeyTransform("b", KeyTransform("1", "value"))),
  557. Q(value__has_key=F("value__1__b")),
  558. ]
  559. for condition in tests:
  560. with self.subTest(condition=condition):
  561. self.assertSequenceEqual(
  562. NullableJSONModel.objects.filter(condition),
  563. [obj],
  564. )
  565. def test_has_keys(self):
  566. self.assertSequenceEqual(
  567. NullableJSONModel.objects.filter(value__has_keys=["a", "c", "h"]),
  568. [self.objs[4]],
  569. )
  570. def test_has_any_keys(self):
  571. self.assertCountEqual(
  572. NullableJSONModel.objects.filter(value__has_any_keys=["c", "l"]),
  573. [self.objs[3], self.objs[4], self.objs[6]],
  574. )
  575. def test_has_key_number(self):
  576. obj = NullableJSONModel.objects.create(
  577. value={
  578. "123": "value",
  579. "nested": {"456": "bar", "lorem": "abc", "999": True},
  580. "array": [{"789": "baz", "777": "def", "ipsum": 200}],
  581. "000": "val",
  582. }
  583. )
  584. tests = [
  585. Q(value__has_key="123"),
  586. Q(value__nested__has_key="456"),
  587. Q(value__array__0__has_key="789"),
  588. Q(value__has_keys=["nested", "123", "array", "000"]),
  589. Q(value__nested__has_keys=["lorem", "999", "456"]),
  590. Q(value__array__0__has_keys=["789", "ipsum", "777"]),
  591. Q(value__has_any_keys=["000", "nonexistent"]),
  592. Q(value__nested__has_any_keys=["999", "nonexistent"]),
  593. Q(value__array__0__has_any_keys=["777", "nonexistent"]),
  594. ]
  595. for condition in tests:
  596. with self.subTest(condition=condition):
  597. self.assertSequenceEqual(
  598. NullableJSONModel.objects.filter(condition),
  599. [obj],
  600. )
  601. @skipUnlessDBFeature("supports_json_field_contains")
  602. def test_contains(self):
  603. tests = [
  604. ({}, self.objs[2:5] + self.objs[6:8]),
  605. ({"baz": {"a": "b", "c": "d"}}, [self.objs[7]]),
  606. ({"baz": {"a": "b"}}, [self.objs[7]]),
  607. ({"baz": {"c": "d"}}, [self.objs[7]]),
  608. ({"k": True, "l": False}, [self.objs[6]]),
  609. ({"d": ["e", {"f": "g"}]}, [self.objs[4]]),
  610. ({"d": ["e"]}, [self.objs[4]]),
  611. ({"d": [{"f": "g"}]}, [self.objs[4]]),
  612. ([1, [2]], [self.objs[5]]),
  613. ([1], [self.objs[5]]),
  614. ([[2]], [self.objs[5]]),
  615. ({"n": [None, True, False]}, [self.objs[4]]),
  616. ({"j": None}, [self.objs[4]]),
  617. ]
  618. for value, expected in tests:
  619. with self.subTest(value=value):
  620. qs = NullableJSONModel.objects.filter(value__contains=value)
  621. self.assertCountEqual(qs, expected)
  622. @skipIfDBFeature("supports_json_field_contains")
  623. def test_contains_unsupported(self):
  624. msg = "contains lookup is not supported on this database backend."
  625. with self.assertRaisesMessage(NotSupportedError, msg):
  626. NullableJSONModel.objects.filter(
  627. value__contains={"baz": {"a": "b", "c": "d"}},
  628. ).get()
  629. @skipUnlessDBFeature(
  630. "supports_primitives_in_json_field",
  631. "supports_json_field_contains",
  632. )
  633. def test_contains_primitives(self):
  634. for value in self.primitives:
  635. with self.subTest(value=value):
  636. qs = NullableJSONModel.objects.filter(value__contains=value)
  637. self.assertIs(qs.exists(), True)
  638. @skipUnlessDBFeature("supports_json_field_contains")
  639. def test_contained_by(self):
  640. qs = NullableJSONModel.objects.filter(
  641. value__contained_by={"a": "b", "c": 14, "h": True}
  642. )
  643. self.assertCountEqual(qs, self.objs[2:4])
  644. @skipIfDBFeature("supports_json_field_contains")
  645. def test_contained_by_unsupported(self):
  646. msg = "contained_by lookup is not supported on this database backend."
  647. with self.assertRaisesMessage(NotSupportedError, msg):
  648. NullableJSONModel.objects.filter(value__contained_by={"a": "b"}).get()
  649. def test_deep_values(self):
  650. qs = NullableJSONModel.objects.values_list("value__k__l").order_by("pk")
  651. expected_objs = [(None,)] * len(self.objs)
  652. expected_objs[4] = ("m",)
  653. self.assertSequenceEqual(qs, expected_objs)
  654. @skipUnlessDBFeature("can_distinct_on_fields")
  655. def test_deep_distinct(self):
  656. query = NullableJSONModel.objects.distinct("value__k__l").values_list(
  657. "value__k__l"
  658. )
  659. expected = [("m",), (None,)]
  660. if not connection.features.nulls_order_largest:
  661. expected.reverse()
  662. self.assertSequenceEqual(query, expected)
  663. def test_isnull_key(self):
  664. # key__isnull=False works the same as has_key='key'.
  665. self.assertCountEqual(
  666. NullableJSONModel.objects.filter(value__a__isnull=True),
  667. self.objs[:3] + self.objs[5:],
  668. )
  669. self.assertCountEqual(
  670. NullableJSONModel.objects.filter(value__j__isnull=True),
  671. self.objs[:4] + self.objs[5:],
  672. )
  673. self.assertCountEqual(
  674. NullableJSONModel.objects.filter(value__a__isnull=False),
  675. [self.objs[3], self.objs[4]],
  676. )
  677. self.assertSequenceEqual(
  678. NullableJSONModel.objects.filter(value__j__isnull=False),
  679. [self.objs[4]],
  680. )
  681. def test_isnull_key_or_none(self):
  682. obj = NullableJSONModel.objects.create(value={"a": None})
  683. self.assertCountEqual(
  684. NullableJSONModel.objects.filter(
  685. Q(value__a__isnull=True) | Q(value__a=None)
  686. ),
  687. self.objs[:3] + self.objs[5:] + [obj],
  688. )
  689. def test_none_key(self):
  690. self.assertSequenceEqual(
  691. NullableJSONModel.objects.filter(value__j=None),
  692. [self.objs[4]],
  693. )
  694. def test_none_key_exclude(self):
  695. obj = NullableJSONModel.objects.create(value={"j": 1})
  696. if connection.vendor == "oracle":
  697. # Oracle supports filtering JSON objects with NULL keys, but the
  698. # current implementation doesn't support it.
  699. self.assertSequenceEqual(
  700. NullableJSONModel.objects.exclude(value__j=None),
  701. self.objs[1:4] + self.objs[5:] + [obj],
  702. )
  703. else:
  704. self.assertSequenceEqual(
  705. NullableJSONModel.objects.exclude(value__j=None), [obj]
  706. )
  707. def test_shallow_list_lookup(self):
  708. self.assertSequenceEqual(
  709. NullableJSONModel.objects.filter(value__0=1),
  710. [self.objs[5]],
  711. )
  712. def test_shallow_obj_lookup(self):
  713. self.assertCountEqual(
  714. NullableJSONModel.objects.filter(value__a="b"),
  715. [self.objs[3], self.objs[4]],
  716. )
  717. def test_obj_subquery_lookup(self):
  718. qs = NullableJSONModel.objects.annotate(
  719. field=Subquery(
  720. NullableJSONModel.objects.filter(pk=OuterRef("pk")).values("value")
  721. ),
  722. ).filter(field__a="b")
  723. self.assertCountEqual(qs, [self.objs[3], self.objs[4]])
  724. def test_deep_lookup_objs(self):
  725. self.assertSequenceEqual(
  726. NullableJSONModel.objects.filter(value__k__l="m"),
  727. [self.objs[4]],
  728. )
  729. def test_shallow_lookup_obj_target(self):
  730. self.assertSequenceEqual(
  731. NullableJSONModel.objects.filter(value__k={"l": "m"}),
  732. [self.objs[4]],
  733. )
  734. def test_deep_lookup_array(self):
  735. self.assertSequenceEqual(
  736. NullableJSONModel.objects.filter(value__1__0=2),
  737. [self.objs[5]],
  738. )
  739. def test_deep_lookup_mixed(self):
  740. self.assertSequenceEqual(
  741. NullableJSONModel.objects.filter(value__d__1__f="g"),
  742. [self.objs[4]],
  743. )
  744. def test_deep_lookup_transform(self):
  745. self.assertCountEqual(
  746. NullableJSONModel.objects.filter(value__c__gt=2),
  747. [self.objs[3], self.objs[4]],
  748. )
  749. self.assertCountEqual(
  750. NullableJSONModel.objects.filter(value__c__gt=2.33),
  751. [self.objs[3], self.objs[4]],
  752. )
  753. self.assertIs(NullableJSONModel.objects.filter(value__c__lt=5).exists(), False)
  754. def test_lookups_special_chars(self):
  755. test_keys = [
  756. "CONTROL",
  757. "single'",
  758. "dollar$",
  759. "dot.dot",
  760. "with space",
  761. "back\\slash",
  762. "question?mark",
  763. "user@name",
  764. "emo🤡'ji",
  765. "com,ma",
  766. "curly{{{brace}}}s",
  767. "escape\uffff'seq'\uffffue\uffff'nce",
  768. ]
  769. json_value = {key: "some value" for key in test_keys}
  770. obj = NullableJSONModel.objects.create(value=json_value)
  771. obj.refresh_from_db()
  772. self.assertEqual(obj.value, json_value)
  773. for key in test_keys:
  774. lookups = {
  775. "has_key": Q(value__has_key=key),
  776. "has_keys": Q(value__has_keys=[key, "CONTROL"]),
  777. "has_any_keys": Q(value__has_any_keys=[key, "does_not_exist"]),
  778. "exact": Q(**{f"value__{key}": "some value"}),
  779. }
  780. for lookup, condition in lookups.items():
  781. results = NullableJSONModel.objects.filter(condition)
  782. with self.subTest(key=key, lookup=lookup):
  783. self.assertSequenceEqual(results, [obj])
  784. def test_lookups_special_chars_double_quotes(self):
  785. test_keys = [
  786. 'double"',
  787. "m\\i@x. m🤡'a,t{{{ch}}}e?d$\"'es\uffff'ca\uffff'pe",
  788. ]
  789. json_value = {key: "some value" for key in test_keys}
  790. obj = NullableJSONModel.objects.create(value=json_value)
  791. obj.refresh_from_db()
  792. self.assertEqual(obj.value, json_value)
  793. self.assertSequenceEqual(
  794. NullableJSONModel.objects.filter(value__has_keys=test_keys), [obj]
  795. )
  796. for key in test_keys:
  797. with self.subTest(key=key):
  798. results = NullableJSONModel.objects.filter(
  799. Q(value__has_key=key),
  800. Q(value__has_any_keys=[key, "does_not_exist"]),
  801. Q(**{f"value__{key}": "some value"}),
  802. )
  803. self.assertSequenceEqual(results, [obj])
  804. def test_lookup_exclude(self):
  805. tests = [
  806. (Q(value__a="b"), [self.objs[0]]),
  807. (Q(value__foo="bax"), [self.objs[0], self.objs[7]]),
  808. ]
  809. for condition, expected in tests:
  810. self.assertCountEqual(
  811. NullableJSONModel.objects.exclude(condition),
  812. expected,
  813. )
  814. self.assertCountEqual(
  815. NullableJSONModel.objects.filter(~condition),
  816. expected,
  817. )
  818. def test_lookup_exclude_nonexistent_key(self):
  819. # Values without the key are ignored.
  820. condition = Q(value__foo="bax")
  821. objs_with_value = [self.objs[6]]
  822. objs_with_different_value = [self.objs[0], self.objs[7]]
  823. self.assertCountEqual(
  824. NullableJSONModel.objects.exclude(condition),
  825. objs_with_different_value,
  826. )
  827. self.assertSequenceEqual(
  828. NullableJSONModel.objects.exclude(~condition),
  829. objs_with_value,
  830. )
  831. self.assertCountEqual(
  832. NullableJSONModel.objects.filter(condition | ~condition),
  833. objs_with_value + objs_with_different_value,
  834. )
  835. self.assertCountEqual(
  836. NullableJSONModel.objects.exclude(condition & ~condition),
  837. objs_with_value + objs_with_different_value,
  838. )
  839. # Add the __isnull lookup to get an exhaustive set.
  840. self.assertCountEqual(
  841. NullableJSONModel.objects.exclude(condition & Q(value__foo__isnull=False)),
  842. self.objs[0:6] + self.objs[7:],
  843. )
  844. self.assertSequenceEqual(
  845. NullableJSONModel.objects.filter(condition & Q(value__foo__isnull=False)),
  846. objs_with_value,
  847. )
  848. def test_usage_in_subquery(self):
  849. self.assertCountEqual(
  850. NullableJSONModel.objects.filter(
  851. id__in=NullableJSONModel.objects.filter(value__c=14),
  852. ),
  853. self.objs[3:5],
  854. )
  855. @skipUnlessDBFeature("supports_json_field_contains")
  856. def test_array_key_contains(self):
  857. tests = [
  858. ([], [self.objs[7]]),
  859. ("bar", [self.objs[7]]),
  860. (["bar"], [self.objs[7]]),
  861. ("ar", []),
  862. ]
  863. for value, expected in tests:
  864. with self.subTest(value=value):
  865. self.assertSequenceEqual(
  866. NullableJSONModel.objects.filter(value__bar__contains=value),
  867. expected,
  868. )
  869. def test_key_iexact(self):
  870. self.assertIs(
  871. NullableJSONModel.objects.filter(value__foo__iexact="BaR").exists(), True
  872. )
  873. self.assertIs(
  874. NullableJSONModel.objects.filter(value__foo__iexact='"BaR"').exists(), False
  875. )
  876. def test_key_in(self):
  877. tests = [
  878. ("value__c__in", [14], self.objs[3:5]),
  879. ("value__c__in", [14, 15], self.objs[3:5]),
  880. ("value__0__in", [1], [self.objs[5]]),
  881. ("value__0__in", [1, 3], [self.objs[5]]),
  882. ("value__foo__in", ["bar"], [self.objs[7]]),
  883. (
  884. "value__foo__in",
  885. [KeyTransform("foo", KeyTransform("bax", "value"))],
  886. [self.objs[7]],
  887. ),
  888. ("value__foo__in", [F("value__bax__foo")], [self.objs[7]]),
  889. (
  890. "value__foo__in",
  891. [KeyTransform("foo", KeyTransform("bax", "value")), "baz"],
  892. [self.objs[7]],
  893. ),
  894. ("value__foo__in", [F("value__bax__foo"), "baz"], [self.objs[7]]),
  895. ("value__foo__in", ["bar", "baz"], [self.objs[7]]),
  896. ("value__bar__in", [["foo", "bar"]], [self.objs[7]]),
  897. ("value__bar__in", [["foo", "bar"], ["a"]], [self.objs[7]]),
  898. ("value__bax__in", [{"foo": "bar"}, {"a": "b"}], [self.objs[7]]),
  899. ("value__h__in", [True, "foo"], [self.objs[4]]),
  900. ("value__i__in", [False, "foo"], [self.objs[4]]),
  901. ]
  902. for lookup, value, expected in tests:
  903. with self.subTest(lookup=lookup, value=value), transaction.atomic():
  904. self.assertCountEqual(
  905. NullableJSONModel.objects.filter(**{lookup: value}),
  906. expected,
  907. )
  908. def test_key_values(self):
  909. qs = NullableJSONModel.objects.filter(value__h=True)
  910. tests = [
  911. ("value__a", "b"),
  912. ("value__c", 14),
  913. ("value__d", ["e", {"f": "g"}]),
  914. ("value__h", True),
  915. ("value__i", False),
  916. ("value__j", None),
  917. ("value__k", {"l": "m"}),
  918. ("value__n", [None, True, False]),
  919. ("value__p", 4.2),
  920. ("value__r", {"s": True, "t": False}),
  921. ]
  922. for lookup, expected in tests:
  923. with self.subTest(lookup=lookup):
  924. self.assertEqual(qs.values_list(lookup, flat=True).get(), expected)
  925. def test_key_values_boolean(self):
  926. qs = NullableJSONModel.objects.filter(value__h=True, value__i=False)
  927. tests = [
  928. ("value__h", True),
  929. ("value__i", False),
  930. ]
  931. for lookup, expected in tests:
  932. with self.subTest(lookup=lookup):
  933. self.assertIs(qs.values_list(lookup, flat=True).get(), expected)
  934. @skipUnlessDBFeature("supports_json_field_contains")
  935. def test_key_contains(self):
  936. self.assertIs(
  937. NullableJSONModel.objects.filter(value__foo__contains="ar").exists(), False
  938. )
  939. self.assertIs(
  940. NullableJSONModel.objects.filter(value__foo__contains="bar").exists(), True
  941. )
  942. def test_key_icontains(self):
  943. self.assertIs(
  944. NullableJSONModel.objects.filter(value__foo__icontains="Ar").exists(), True
  945. )
  946. def test_key_startswith(self):
  947. self.assertIs(
  948. NullableJSONModel.objects.filter(value__foo__startswith="b").exists(), True
  949. )
  950. def test_key_istartswith(self):
  951. self.assertIs(
  952. NullableJSONModel.objects.filter(value__foo__istartswith="B").exists(), True
  953. )
  954. def test_key_endswith(self):
  955. self.assertIs(
  956. NullableJSONModel.objects.filter(value__foo__endswith="r").exists(), True
  957. )
  958. def test_key_iendswith(self):
  959. self.assertIs(
  960. NullableJSONModel.objects.filter(value__foo__iendswith="R").exists(), True
  961. )
  962. def test_key_regex(self):
  963. self.assertIs(
  964. NullableJSONModel.objects.filter(value__foo__regex=r"^bar$").exists(), True
  965. )
  966. def test_key_iregex(self):
  967. self.assertIs(
  968. NullableJSONModel.objects.filter(value__foo__iregex=r"^bAr$").exists(), True
  969. )
  970. def test_key_quoted_string(self):
  971. self.assertEqual(
  972. NullableJSONModel.objects.filter(value__o='"quoted"').get(),
  973. self.objs[4],
  974. )
  975. @skipUnlessDBFeature("has_json_operators")
  976. def test_key_sql_injection(self):
  977. with CaptureQueriesContext(connection) as queries:
  978. self.assertIs(
  979. NullableJSONModel.objects.filter(
  980. **{
  981. """value__test' = '"a"') OR 1 = 1 OR ('d""": "x",
  982. }
  983. ).exists(),
  984. False,
  985. )
  986. self.assertIn(
  987. """."value" -> 'test'' = ''"a"'') OR 1 = 1 OR (''d') = '"x"'""",
  988. queries[0]["sql"],
  989. )
  990. @skipIfDBFeature("has_json_operators")
  991. def test_key_sql_injection_escape(self):
  992. query = str(
  993. JSONModel.objects.filter(
  994. **{
  995. """value__test") = '"a"' OR 1 = 1 OR ("d""": "x",
  996. }
  997. ).query
  998. )
  999. self.assertIn('"test\\"', query)
  1000. self.assertIn('\\"d', query)
  1001. def test_key_escape(self):
  1002. obj = NullableJSONModel.objects.create(value={"%total": 10})
  1003. self.assertEqual(
  1004. NullableJSONModel.objects.filter(**{"value__%total": 10}).get(), obj
  1005. )
  1006. def test_none_key_and_exact_lookup(self):
  1007. self.assertSequenceEqual(
  1008. NullableJSONModel.objects.filter(value__a="b", value__j=None),
  1009. [self.objs[4]],
  1010. )
  1011. def test_lookups_with_key_transform(self):
  1012. tests = (
  1013. ("value__baz__has_key", "c"),
  1014. ("value__baz__has_keys", ["a", "c"]),
  1015. ("value__baz__has_any_keys", ["a", "x"]),
  1016. ("value__has_key", KeyTextTransform("foo", "value")),
  1017. )
  1018. for lookup, value in tests:
  1019. with self.subTest(lookup=lookup):
  1020. self.assertIs(
  1021. NullableJSONModel.objects.filter(
  1022. **{lookup: value},
  1023. ).exists(),
  1024. True,
  1025. )
  1026. @skipUnlessDBFeature("supports_json_field_contains")
  1027. def test_contains_contained_by_with_key_transform(self):
  1028. tests = [
  1029. ("value__d__contains", "e"),
  1030. ("value__d__contains", [{"f": "g"}]),
  1031. ("value__contains", KeyTransform("bax", "value")),
  1032. ("value__contains", F("value__bax")),
  1033. ("value__baz__contains", {"a": "b"}),
  1034. ("value__baz__contained_by", {"a": "b", "c": "d", "e": "f"}),
  1035. (
  1036. "value__contained_by",
  1037. KeyTransform(
  1038. "x",
  1039. RawSQL(
  1040. self.raw_sql,
  1041. ['{"x": {"a": "b", "c": 1, "d": "e"}}'],
  1042. ),
  1043. ),
  1044. ),
  1045. ]
  1046. # For databases where {'f': 'g'} (without surrounding []) matches
  1047. # [{'f': 'g'}].
  1048. if not connection.features.json_key_contains_list_matching_requires_list:
  1049. tests.append(("value__d__contains", {"f": "g"}))
  1050. for lookup, value in tests:
  1051. with self.subTest(lookup=lookup, value=value):
  1052. self.assertIs(
  1053. NullableJSONModel.objects.filter(
  1054. **{lookup: value},
  1055. ).exists(),
  1056. True,
  1057. )
  1058. def test_join_key_transform_annotation_expression(self):
  1059. related_obj = RelatedJSONModel.objects.create(
  1060. value={"d": ["f", "e"]},
  1061. json_model=self.objs[4],
  1062. )
  1063. RelatedJSONModel.objects.create(
  1064. value={"d": ["e", "f"]},
  1065. json_model=self.objs[4],
  1066. )
  1067. self.assertSequenceEqual(
  1068. RelatedJSONModel.objects.annotate(
  1069. key=F("value__d"),
  1070. related_key=F("json_model__value__d"),
  1071. chain=F("key__1"),
  1072. expr=Cast("key", models.JSONField()),
  1073. ).filter(chain=F("related_key__0")),
  1074. [related_obj],
  1075. )
  1076. def test_key_text_transform_from_lookup(self):
  1077. qs = NullableJSONModel.objects.annotate(b=KT("value__bax__foo")).filter(
  1078. b__contains="ar",
  1079. )
  1080. self.assertSequenceEqual(qs, [self.objs[7]])
  1081. qs = NullableJSONModel.objects.annotate(c=KT("value__o")).filter(
  1082. c__contains="uot",
  1083. )
  1084. self.assertSequenceEqual(qs, [self.objs[4]])
  1085. def test_key_text_transform_from_lookup_invalid(self):
  1086. msg = "Lookup must contain key or index transforms."
  1087. with self.assertRaisesMessage(ValueError, msg):
  1088. KT("value")
  1089. with self.assertRaisesMessage(ValueError, msg):
  1090. KT("")
  1091. def test_literal_annotation_filtering(self):
  1092. all_objects = NullableJSONModel.objects.order_by("id")
  1093. qs = all_objects.annotate(data=Value({"foo": "bar"}, JSONField())).filter(
  1094. data__foo="bar"
  1095. )
  1096. self.assertQuerySetEqual(qs, all_objects)