123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- from decimal import Decimal
- import django
- from django import forms
- from django.core.serializers import deserialize, serialize
- from django.core.serializers.base import DeserializationError
- from django.contrib.contenttypes.fields import GenericForeignKey
- from django.contrib.contenttypes.models import ContentType
- from django.db import models
- from django.test import TestCase
- try:
- import json
- except ImportError:
- from django.utils import simplejson as json
-
- from .fields import JSONField, JSONCharField
- try:
- from django.forms.utils import ValidationError
- except ImportError:
- from django.forms.util import ValidationError
-
- from django.utils.six import string_types
-
- from collections import OrderedDict
-
-
- class JsonModel(models.Model):
- json = JSONField()
- default_json = JSONField(default={"check": 12})
- complex_default_json = JSONField(default=[{"checkcheck": 1212}])
- empty_default = JSONField(default={})
-
-
- class GenericForeignKeyObj(models.Model):
- name = models.CharField('Foreign Obj', max_length=255, null=True)
-
-
- class JSONModelWithForeignKey(models.Model):
- json = JSONField(null=True)
- foreign_obj = GenericForeignKey()
- object_id = models.PositiveIntegerField(blank=True, null=True, db_index=True)
- content_type = models.ForeignKey(ContentType, blank=True, null=True,
- on_delete=models.CASCADE)
-
-
- class JsonCharModel(models.Model):
- json = JSONCharField(max_length=100)
- default_json = JSONCharField(max_length=100, default={"check": 34})
-
-
- class ComplexEncoder(json.JSONEncoder):
- def default(self, obj):
- if isinstance(obj, complex):
- return {
- '__complex__': True,
- 'real': obj.real,
- 'imag': obj.imag,
- }
-
- return json.JSONEncoder.default(self, obj)
-
-
- def as_complex(dct):
- if '__complex__' in dct:
- return complex(dct['real'], dct['imag'])
- return dct
-
-
- class JSONModelCustomEncoders(models.Model):
- # A JSON field that can store complex numbers
- json = JSONField(
- dump_kwargs={'cls': ComplexEncoder, "indent": 4},
- load_kwargs={'object_hook': as_complex},
- )
-
-
- class JSONModelWithForeignKeyTestCase(TestCase):
- def test_object_create(self):
- foreign_obj = GenericForeignKeyObj.objects.create(name='Brain')
- JSONModelWithForeignKey.objects.create(foreign_obj=foreign_obj)
-
-
- class JSONFieldTest(TestCase):
- """JSONField Wrapper Tests"""
-
- json_model = JsonModel
-
- def test_json_field_create(self):
- """Test saving a JSON object in our JSONField"""
- json_obj = {
- "item_1": "this is a json blah",
- "blergh": "hey, hey, hey"}
-
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_string_in_json_field(self):
- """Test saving an ordinary Python string in our JSONField"""
- json_obj = 'blah blah'
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_float_in_json_field(self):
- """Test saving a Python float in our JSONField"""
- json_obj = 1.23
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_int_in_json_field(self):
- """Test saving a Python integer in our JSONField"""
- json_obj = 1234567
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_decimal_in_json_field(self):
- """Test saving a Python Decimal in our JSONField"""
- json_obj = Decimal(12.34)
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- # here we must know to convert the returned string back to Decimal,
- # since json does not support that format
- self.assertEqual(Decimal(new_obj.json), json_obj)
-
- def test_json_field_modify(self):
- """Test modifying a JSON object in our JSONField"""
- json_obj_1 = {'a': 1, 'b': 2}
- json_obj_2 = {'a': 3, 'b': 4}
-
- obj = self.json_model.objects.create(json=json_obj_1)
- self.assertEqual(obj.json, json_obj_1)
- obj.json = json_obj_2
-
- self.assertEqual(obj.json, json_obj_2)
- obj.save()
- self.assertEqual(obj.json, json_obj_2)
-
- self.assertTrue(obj)
-
- def test_json_field_load(self):
- """Test loading a JSON object from the DB"""
- json_obj_1 = {'a': 1, 'b': 2}
- obj = self.json_model.objects.create(json=json_obj_1)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj_1)
-
- def test_json_list(self):
- """Test storing a JSON list"""
- json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
-
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
- self.assertEqual(new_obj.json, json_obj)
-
- def test_empty_objects(self):
- """Test storing empty objects"""
- for json_obj in [{}, [], 0, '', False]:
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
- self.assertEqual(json_obj, obj.json)
- self.assertEqual(json_obj, new_obj.json)
-
- def test_custom_encoder(self):
- """Test encoder_cls and object_hook"""
- value = 1 + 3j # A complex number
-
- obj = JSONModelCustomEncoders.objects.create(json=value)
- new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
- self.assertEqual(value, new_obj.json)
-
- def test_django_serializers(self):
- """Test serializing/deserializing jsonfield data"""
- for json_obj in [{}, [], 0, '', False, {'key': 'value', 'num': 42,
- 'ary': list(range(5)),
- 'dict': {'k': 'v'}}]:
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
- self.assert_(new_obj)
-
- queryset = self.json_model.objects.all()
- ser = serialize('json', queryset)
- for dobj in deserialize('json', ser):
- obj = dobj.object
- pulled = self.json_model.objects.get(id=obj.pk)
- self.assertEqual(obj.json, pulled.json)
-
- def test_default_parameters(self):
- """Test providing a default value to the model"""
- model = JsonModel()
- model.json = {"check": 12}
- self.assertEqual(model.json, {"check": 12})
- self.assertEqual(type(model.json), dict)
-
- self.assertEqual(model.default_json, {"check": 12})
- self.assertEqual(type(model.default_json), dict)
-
- def test_invalid_json(self):
- # invalid json data {] in the json and default_json fields
- ser = '[{"pk": 1, "model": "jsonfield.jsoncharmodel", ' \
- '"fields": {"json": "{]", "default_json": "{]"}}]'
- with self.assertRaises(DeserializationError) as cm:
- next(deserialize('json', ser))
- # Django 2.0+ uses PEP 3134 exception chaining
- if django.VERSION < (2, 0,):
- inner = cm.exception.args[0]
- else:
- inner = cm.exception.__context__
- self.assertTrue(isinstance(inner, ValidationError))
- self.assertEqual('Enter valid JSON', inner.messages[0])
-
- def test_integer_in_string_in_json_field(self):
- """Test saving the Python string '123' in our JSONField"""
- json_obj = '123'
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_boolean_in_string_in_json_field(self):
- """Test saving the Python string 'true' in our JSONField"""
- json_obj = 'true'
- obj = self.json_model.objects.create(json=json_obj)
- new_obj = self.json_model.objects.get(id=obj.id)
-
- self.assertEqual(new_obj.json, json_obj)
-
- def test_pass_by_reference_pollution(self):
- """Make sure the default parameter is copied rather than passed by reference"""
- model = JsonModel()
- model.default_json["check"] = 144
- model.complex_default_json[0]["checkcheck"] = 144
- self.assertEqual(model.default_json["check"], 144)
- self.assertEqual(model.complex_default_json[0]["checkcheck"], 144)
-
- # Make sure when we create a new model, it resets to the default value
- # and not to what we just set it to (it would be if it were passed by reference)
- model = JsonModel()
- self.assertEqual(model.default_json["check"], 12)
- self.assertEqual(model.complex_default_json[0]["checkcheck"], 1212)
-
- def test_normal_regex_filter(self):
- """Make sure JSON model can filter regex"""
-
- JsonModel.objects.create(json={"boom": "town"})
- JsonModel.objects.create(json={"move": "town"})
- JsonModel.objects.create(json={"save": "town"})
-
- self.assertEqual(JsonModel.objects.count(), 3)
-
- self.assertEqual(JsonModel.objects.filter(json__regex=r"boom").count(), 1)
- self.assertEqual(JsonModel.objects.filter(json__regex=r"town").count(), 3)
-
- def test_save_blank_object(self):
- """Test that JSON model can save a blank object as none"""
-
- model = JsonModel()
- self.assertEqual(model.empty_default, {})
-
- model.save()
- self.assertEqual(model.empty_default, {})
-
- model1 = JsonModel(empty_default={"hey": "now"})
- self.assertEqual(model1.empty_default, {"hey": "now"})
-
- model1.save()
- self.assertEqual(model1.empty_default, {"hey": "now"})
-
-
- class JSONCharFieldTest(JSONFieldTest):
- json_model = JsonCharModel
-
-
- class OrderedJsonModel(models.Model):
- json = JSONField(load_kwargs={'object_pairs_hook': OrderedDict})
-
-
- class OrderedDictSerializationTest(TestCase):
- def setUp(self):
- self.ordered_dict = OrderedDict([
- ('number', [1, 2, 3, 4]),
- ('notes', True),
- ('alpha', True),
- ('romeo', True),
- ('juliet', True),
- ('bravo', True),
- ])
- self.expected_key_order = ['number', 'notes', 'alpha', 'romeo', 'juliet', 'bravo']
-
- def test_ordered_dict_differs_from_normal_dict(self):
- self.assertEqual(list(self.ordered_dict.keys()), self.expected_key_order)
- self.assertNotEqual(dict(self.ordered_dict).keys(), self.expected_key_order)
-
- def test_default_behaviour_loses_sort_order(self):
- mod = JsonModel.objects.create(json=self.ordered_dict)
- self.assertEqual(list(mod.json.keys()), self.expected_key_order)
- mod_from_db = JsonModel.objects.get(id=mod.id)
-
- # mod_from_db lost ordering information during json.loads()
- self.assertNotEqual(mod_from_db.json.keys(), self.expected_key_order)
-
- def test_load_kwargs_hook_does_not_lose_sort_order(self):
- mod = OrderedJsonModel.objects.create(json=self.ordered_dict)
- self.assertEqual(list(mod.json.keys()), self.expected_key_order)
- mod_from_db = OrderedJsonModel.objects.get(id=mod.id)
- self.assertEqual(list(mod_from_db.json.keys()), self.expected_key_order)
-
-
- class JsonNotRequiredModel(models.Model):
- json = JSONField(blank=True, null=True)
-
-
- class JsonNotRequiredForm(forms.ModelForm):
- class Meta:
- model = JsonNotRequiredModel
- fields = '__all__'
-
-
- class JsonModelFormTest(TestCase):
- def test_blank_form(self):
- form = JsonNotRequiredForm(data={'json': ''})
- self.assertFalse(form.has_changed())
-
- def test_form_with_data(self):
- form = JsonNotRequiredForm(data={'json': '{}'})
- self.assertTrue(form.has_changed())
-
-
- class TestFieldAPIMethods(TestCase):
- def test_get_db_prep_value_method_with_null(self):
- json_field_instance = JSONField(null=True)
- value = {'a': 1}
- prepared_value = json_field_instance.get_db_prep_value(
- value, connection=None, prepared=False)
- self.assertIsInstance(prepared_value, string_types)
- self.assertDictEqual(value, json.loads(prepared_value))
- self.assertIs(json_field_instance.get_db_prep_value(
- None, connection=None, prepared=True), None)
- self.assertIs(json_field_instance.get_db_prep_value(
- None, connection=None, prepared=False), None)
-
- def test_get_db_prep_value_method_with_not_null(self):
- json_field_instance = JSONField(null=False)
- value = {'a': 1}
- prepared_value = json_field_instance.get_db_prep_value(
- value, connection=None, prepared=False)
- self.assertIsInstance(prepared_value, string_types)
- self.assertDictEqual(value, json.loads(prepared_value))
- self.assertIs(json_field_instance.get_db_prep_value(
- None, connection=None, prepared=True), None)
- self.assertEqual(json_field_instance.get_db_prep_value(
- None, connection=None, prepared=False), 'null')
-
- def test_get_db_prep_value_method_skips_prepared_values(self):
- json_field_instance = JSONField(null=False)
- value = {'a': 1}
- prepared_value = json_field_instance.get_db_prep_value(
- value, connection=None, prepared=True)
- self.assertIs(prepared_value, value)
-
- def test_get_prep_value_always_json_dumps_if_not_null(self):
- json_field_instance = JSONField(null=False)
- value = {'a': 1}
- prepared_value = json_field_instance.get_prep_value(value)
- self.assertIsInstance(prepared_value, string_types)
- self.assertDictEqual(value, json.loads(prepared_value))
- already_json = json.dumps(value)
- double_prepared_value = json_field_instance.get_prep_value(
- already_json)
- self.assertDictEqual(value,
- json.loads(json.loads(double_prepared_value)))
- self.assertEqual(json_field_instance.get_prep_value(None), 'null')
-
- def test_get_prep_value_can_return_none_if_null(self):
- json_field_instance = JSONField(null=True)
- value = {'a': 1}
- prepared_value = json_field_instance.get_prep_value(value)
- self.assertIsInstance(prepared_value, string_types)
- self.assertDictEqual(value, json.loads(prepared_value))
- already_json = json.dumps(value)
- double_prepared_value = json_field_instance.get_prep_value(
- already_json)
- self.assertDictEqual(value,
- json.loads(json.loads(double_prepared_value)))
- self.assertIs(json_field_instance.get_prep_value(None), None)
|