mirror of
https://github.com/Hopiu/django-notifications.git
synced 2026-03-22 08:10:23 +00:00
392 lines
15 KiB
Python
392 lines
15 KiB
Python
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)
|