Repository: encode/django-rest-framework

Issue:
Support PostgreSQL JSONField
hi  thank you very much
,
DRF not support JSONField,  but Django 1.9 support JSONField

url: https://docs.djangoproject.com/en/dev/ref/contrib/postgres/fields/#jsonfield

i use django 1.9 ,DRF 3.13 ,Postgresql 9.4
## My database  table fields: [i use postgresql]

```
 id<int> , jsonInfo<jsonb>

1,{"username": "dddd","pass":"fffff"}
2,{"username": "1111","pass":"22222"}
3,{"username": "33333","pass":"444444"}
```

---

i created custom JSONField      in DRF 

---

```
class JSONField(serializers.ReadOnlyField):

    def to_native(self, obj):
        return json.dumps(obj)

    def from_native(self, value):
        return json.loads(value)
```

---

My Serializer:

---

```
class tradeSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(read_only=True)
    username= JSONField(source='jsonInfo__username')
```

---

then  :

 i  run  

```
curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/tr/
```

i can get  'id' field value, but  i  canot get username value, it's return null

---

if i changed My  Serializer code like this:

---

```
class tradeSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(read_only=True)
    username= JSONField(source='jsonInfo')
```

---

 i  run  

```
curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/tr/
```

 is it ok!
but it's return all jsonInfo field  value

  i   hope     , i can  use    JSONField(source='jsonInfo__username')

i only  want  get   username

can you help me ?

thank you very much

:)



Discussion:
According to the Roadmap https://code.djangoproject.com/wiki/Version1.9Roadmap the alpha for 1.9 is not due for another two months. Final release is a while after that. DRF support for JSONField will be in line with that.

@carltongibson thank you   :)

Isn't the JSONField already supported with the DictField?

There's still at least _some_ work to do here...
- We probably want to validate that only valid JSON native datatypes are present in the data structure.
- We _may_ want to treat string and binary inputs as raw JSON and parse them.
- We need to map the JSONField model field onto a serializer field by default.

But yes, using DictField will give you 90% of what you need already.


Resolving patch:
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -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
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -5,6 +5,7 @@
 import datetime
 import decimal
 import inspect
+import json
 import re
 import uuid
 from collections import OrderedDict
@@ -1522,6 +1523,37 @@ 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:
+                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...
 
 class ReadOnlyField(Field):
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -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 (
@@ -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


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -1525,6 +1525,58 @@ 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 = [
+        (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.
 # ---------------------