Added JSONField. Closes #3170.

This commit is contained in:
Tom Christie 2015-09-28 17:25:52 +01:00
parent bae47b7f36
commit 10dbf1316f
5 changed files with 100 additions and 0 deletions

View File

@ -459,6 +459,14 @@ You can also use the declarative style, as with `ListField`. For example:
class DocumentField(DictField):
child = CharField()
## JSONField
A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON encoded strings.
**Signature**: `JSONField(binary)`
- `binary` - If set to `True` then the field will output and validate a JSON encoded string, rather that a primitive data structure. Defaults to `False`.
---
# Miscellaneous fields

View File

@ -64,6 +64,13 @@ except ImportError:
postgres_fields = None
# JSONField is only supported from 1.9 onwards
try:
from django.contrib.postgres.fields import JSONField
except ImportError:
JSONField = None
# django-filter is optional
try:
import django_filters

View File

@ -5,6 +5,7 @@ import copy
import datetime
import decimal
import inspect
import json
import re
import uuid
from collections import OrderedDict
@ -1522,6 +1523,31 @@ class DictField(Field):
])
class JSONField(Field):
default_error_messages = {
'invalid': _('Value must be valid JSON.')
}
def __init__(self, *args, **kwargs):
self.binary = kwargs.pop('binary', False)
super(JSONField, self).__init__(*args, **kwargs)
def to_internal_value(self, data):
try:
if self.binary:
return json.loads(data)
else:
json.dumps(data)
except (TypeError, ValueError):
self.fail('invalid')
return data
def to_representation(self, value):
if self.binary:
return json.dumps(value)
return value
# Miscellaneous field types...
class ReadOnlyField(Field):

View File

@ -21,6 +21,7 @@ from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import DurationField as ModelDurationField
from rest_framework.compat import JSONField as ModelJSONField
from rest_framework.compat import postgres_fields, unicode_to_repr
from rest_framework.utils import model_meta
from rest_framework.utils.field_mapping import (
@ -790,6 +791,8 @@ class ModelSerializer(Serializer):
}
if ModelDurationField is not None:
serializer_field_mapping[ModelDurationField] = DurationField
if ModelJSONField is not None:
serializer_field_mapping[ModelJSONField] = JSONField
serializer_related_field = PrimaryKeyRelatedField
serializer_url_field = HyperlinkedIdentityField
serializer_choice_field = ChoiceField

View File

@ -1525,6 +1525,62 @@ class TestUnvalidatedDictField(FieldValues):
field = serializers.DictField()
class TestJSONField(FieldValues):
"""
Values for `JSONField`.
"""
valid_inputs = [
({
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': None
}, {
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': None
}),
]
invalid_inputs = [
({'a': set()}, ['Value must be valid JSON.']),
]
outputs = [
({
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': 3
}, {
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': 3
}),
]
field = serializers.JSONField()
class TestBinaryJSONField(FieldValues):
"""
Values for `JSONField` with binary=True.
"""
valid_inputs = [
('{"a": 1, "3": null, "b": ["some", "list", true, 1.23]}', {
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': None
}),
]
invalid_inputs = [
('{"a": "unterminated string}', ['Value must be valid JSON.']),
]
outputs = [
({
'a': 1,
'b': ['some', 'list', True, 1.23],
'3': None
}, '{"a": 1, "3": null, "b": ["some", "list", true, 1.23]}'),
]
field = serializers.JSONField(binary=True)
# Tests for FieldField.
# ---------------------