Commit de5732f3 by Tom Christie

Merge pull request #3454 from tomchristie/json-field

Added JSONField.
parents bae47b7f 265ec8ac
...@@ -459,6 +459,14 @@ You can also use the declarative style, as with `ListField`. For example: ...@@ -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 binary 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
......
...@@ -64,6 +64,13 @@ except ImportError: ...@@ -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
......
...@@ -5,6 +5,7 @@ import copy ...@@ -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,37 @@ class DictField(Field): ...@@ -1522,6 +1523,37 @@ 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:
if isinstance(data, six.binary_type):
data = data.decode('utf-8')
return json.loads(data)
else:
json.dumps(data)
except (TypeError, ValueError):
self.fail('invalid')
return data
def to_representation(self, value):
if self.binary:
value = json.dumps(value)
# On python 2.x the return type for json.dumps() is underspecified.
# On python 3.x json.dumps() returns unicode strings.
if isinstance(value, six.text_type):
value = bytes(value.encode('utf-8'))
return value
# Miscellaneous field types... # Miscellaneous field types...
class ReadOnlyField(Field): class ReadOnlyField(Field):
......
...@@ -21,6 +21,7 @@ from django.utils.functional import cached_property ...@@ -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): ...@@ -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
......
...@@ -1525,6 +1525,58 @@ class TestUnvalidatedDictField(FieldValues): ...@@ -1525,6 +1525,58 @@ 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 = [
(b'{"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 = [
(['some', 'list', True, 1.23], b'["some", "list", true, 1.23]'),
]
field = serializers.JSONField(binary=True)
# Tests for FieldField. # Tests for FieldField.
# --------------------- # ---------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment