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): class DocumentField(DictField):
child = CharField() 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 # Miscellaneous fields

View File

@ -64,6 +64,13 @@ except ImportError:
postgres_fields = None 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 # django-filter is optional
try: try:
import django_filters import django_filters

View File

@ -5,6 +5,7 @@ import copy
import datetime import datetime
import decimal import decimal
import inspect import inspect
import json
import re import re
import uuid import uuid
from collections import OrderedDict 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... # Miscellaneous field types...
class ReadOnlyField(Field): 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 django.utils.translation import ugettext_lazy as _
from rest_framework.compat import DurationField as ModelDurationField 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.compat import postgres_fields, unicode_to_repr
from rest_framework.utils import model_meta from rest_framework.utils import model_meta
from rest_framework.utils.field_mapping import ( from rest_framework.utils.field_mapping import (
@ -790,6 +791,8 @@ class ModelSerializer(Serializer):
} }
if ModelDurationField is not None: if ModelDurationField is not None:
serializer_field_mapping[ModelDurationField] = DurationField serializer_field_mapping[ModelDurationField] = DurationField
if ModelJSONField is not None:
serializer_field_mapping[ModelJSONField] = JSONField
serializer_related_field = PrimaryKeyRelatedField serializer_related_field = PrimaryKeyRelatedField
serializer_url_field = HyperlinkedIdentityField serializer_url_field = HyperlinkedIdentityField
serializer_choice_field = ChoiceField serializer_choice_field = ChoiceField

View File

@ -1525,6 +1525,62 @@ class TestUnvalidatedDictField(FieldValues):
field = serializers.DictField() 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. # Tests for FieldField.
# --------------------- # ---------------------