Fixed #25995 -- Added an encoder option to JSONField

Thanks Berker Peksag and Tim Graham for the reviews.
This commit is contained in:
Claude Paroz 2016-08-11 21:05:52 +02:00
parent 989f6108d3
commit 13c3e5d5a0
7 changed files with 98 additions and 10 deletions

View file

@ -23,6 +23,10 @@ except ImportError:
})
return name, path, args, kwargs
class DummyJSONField(models.Field):
def __init__(self, encoder=None, **kwargs):
super(DummyJSONField, self).__init__(**kwargs)
ArrayField = DummyArrayField
BigIntegerRangeField = models.Field
DateRangeField = models.Field
@ -30,5 +34,5 @@ except ImportError:
FloatRangeField = models.Field
HStoreField = models.Field
IntegerRangeField = models.Field
JSONField = models.Field
JSONField = DummyJSONField
SearchVectorField = models.Field

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.core.serializers.json import DjangoJSONEncoder
from django.db import migrations, models
from ..fields import (
@ -223,6 +224,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('field', JSONField(null=True, blank=True)),
('field_custom', JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)),
],
options={
},

View file

@ -1,3 +1,4 @@
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection, models
from .fields import (
@ -132,6 +133,7 @@ class RangeLookupsModel(PostgreSQLModel):
if connection.vendor == 'postgresql' and connection.pg_version >= 90400:
class JSONModel(models.Model):
field = JSONField(blank=True, null=True)
field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder)
else:
# create an object with this name so we don't have failing imports
class JSONModel(object):

View file

@ -1,7 +1,12 @@
from __future__ import unicode_literals
import datetime
import unittest
import uuid
from decimal import Decimal
from django.core import exceptions, serializers
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection
from django.forms import CharField, Form, widgets
from django.test import TestCase
@ -79,6 +84,27 @@ class TestSaveLoad(TestCase):
loaded = JSONModel.objects.get()
self.assertEqual(loaded.field, obj)
def test_custom_encoding(self):
"""
JSONModel.field_custom has a custom DjangoJSONEncoder.
"""
some_uuid = uuid.uuid4()
obj_before = {
'date': datetime.date(2016, 8, 12),
'datetime': datetime.datetime(2016, 8, 12, 13, 44, 47, 575981),
'decimal': Decimal('10.54'),
'uuid': some_uuid,
}
obj_after = {
'date': '2016-08-12',
'datetime': '2016-08-12T13:44:47.575',
'decimal': '10.54',
'uuid': str(some_uuid),
}
JSONModel.objects.create(field_custom=obj_before)
loaded = JSONModel.objects.get()
self.assertEqual(loaded.field_custom, obj_after)
@skipUnlessPG94
class TestQuerying(TestCase):
@ -215,7 +241,10 @@ class TestQuerying(TestCase):
@skipUnlessPG94
class TestSerialization(TestCase):
test_data = '[{"fields": {"field": {"a": "b", "c": null}}, "model": "postgres_tests.jsonmodel", "pk": null}]'
test_data = (
'[{"fields": {"field": {"a": "b", "c": null}, "field_custom": null}, '
'"model": "postgres_tests.jsonmodel", "pk": null}]'
)
def test_dumping(self):
instance = JSONModel(field={'a': 'b', 'c': None})
@ -236,6 +265,12 @@ class TestValidation(PostgreSQLTestCase):
self.assertEqual(cm.exception.code, 'invalid')
self.assertEqual(cm.exception.message % cm.exception.params, "Value must be valid JSON.")
def test_custom_encoder(self):
with self.assertRaisesMessage(ValueError, "The encoder parameter must be a callable object."):
field = JSONField(encoder=DjangoJSONEncoder())
field = JSONField(encoder=DjangoJSONEncoder)
self.assertEqual(field.clean(datetime.timedelta(days=1), None), datetime.timedelta(days=1))
class TestFormField(PostgreSQLTestCase):