Skip to content

Commit

Permalink
Added JSONField. Closes #3170.
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie committed Sep 28, 2015
1 parent bae47b7 commit 10dbf13
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 0 deletions.
8 changes: 8 additions & 0 deletions docs/api-guide/fields.md
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions rest_framework/compat.py
Expand Up @@ -64,6 +64,13 @@ def distinct(queryset, base):
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
Expand Down
26 changes: 26 additions & 0 deletions rest_framework/fields.py
Expand Up @@ -5,6 +5,7 @@
import datetime
import decimal
import inspect
import json
import re
import uuid
from collections import OrderedDict
Expand Down Expand Up @@ -1522,6 +1523,31 @@ def to_representation(self, value):
])


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):
Expand Down
3 changes: 3 additions & 0 deletions rest_framework/serializers.py
Expand Up @@ -21,6 +21,7 @@
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 (
Expand Down Expand Up @@ -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
Expand Down
56 changes: 56 additions & 0 deletions tests/test_fields.py
Expand Up @@ -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.
# ---------------------

Expand Down

0 comments on commit 10dbf13

Please sign in to comment.