Repository: encode/django-rest-framework

Issue:
JSONField form data in a related model renders as python dict
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce

Assuming the following models, serializers and viewsets:

```
# models.py

from django.db import models
from django.contrib.postgres.fields import JSONField

class RelatedModel(models.Model):
    json_content = JSONField()


class MainModel(models.Model):
    name = models.CharField(max_length=64)
    related_object = models.ForeignKey(
        to=RelatedModel,
        on_delete=models.CASCADE
    )

# serializers.py
class RelatedModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = RelatedModel
        fields = '__all__'


class MainModelSerializer(serializers.ModelSerializer):
    related_object = RelatedModelSerializer()

    class Meta:
        model = MainModel
        fields = (
            'id', 'name', 'related_object'
        )

# views.py
class MainModelViewSet(viewsets.ModelViewSet):
    serializer_class = MainModelSerializer
    queryset = MainModel.objects.all()


class RelatedModelViewSet(viewsets.ModelViewSet):
    serializer_class = RelatedModelSerializer
    queryset = RelatedModel.objects.all()

# Create models in shell:

rm = RelatedModel.objects.create(json_content={'bool': False, 'text': 'Test text'})
MainModel.objects.create(name='Main', related_object=rm)
```

Then, open the MainModel instance in the Browsable API and look for the content in the form.


## Expected behavior

Form data for the `json_content` field to be in JSON format, that is: 
```
{
    "bool": false,
    "text": "Test text"
}
```

## Actual behavior
Json content rendered as a python dict, that is `{'bool': False, 'text': 'Test text'}`, as shown in:
![screenshot from 2018-09-25 18-01-56](https://user-images.githubusercontent.com/220983/46023361-24ae8780-c0ed-11e8-8569-fd1f6ed69615.png)


When viewing the RelatedModel itself (using RelatedModelViewSet) the json content is rendered fine.



Discussion:
Closing it as duplicate of #4999 
I disagree with this being a duplicate of #4999, because that issue was fixed and the JSONField renders correctly when the field is in the serialized model directly. This issue occurs when the JSONField is in the related model.
Is there a workaround for this problem?, I think the same problems occurs with nested serializers in my REST Api output. The JsonField in the related entity is returned as string with a python dict and not is valid JSON.
in [serializers#549](https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py#L549) when a field is a Serializer, thats the case with related_object, it returns a `NestedBoundField` to parse it.
after that all fields found by `NestedBoundField` will be parsed as a `BoundField`, because `NestedBoundField` can't identify if the field is a json field.

You can see this here in [serializer_helpers#119](https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/serializer_helpers.py#L119)

as a workaround, i did this inside `BoundField.as_form_field`, put the code from JSONBoundField that parses the json from `value` inside it:
```Python
# serializer_helpers#81
def as_form_field(self):
    value = '' if (self.value is None or self.value is False) else self.value
    # if value is a valid json then we parse it
    if not getattr(value, 'is_json_string', False):
        try:
            value = json.dumps(self.value, sort_keys=True, indent=4)
        except (TypeError, ValueError):
            pass

    return self.__class__(self._field, value, self.errors, self._prefix)
```

and it rendered as expected:
![image](https://user-images.githubusercontent.com/9954401/46918543-928df680-cfa9-11e8-8815-b098627ef1ec.png)

i think thats not the best solution, but now the problem's found. I woudl like to help with a PR to fix this, if you could give me instructions.

sorry for my bad english, thats all.
Yes, this workaround works for this case but I'm afraid it might break something else. But it gave me the idea that by adapting the `Serializer.__getitem__` logic into `NestedBoundField.__getitem__`, the JSONBoundField would be used properly.

Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1766,6 +1766,9 @@ class JSONField(Field):
         'invalid': _('Value must be valid JSON.')
     }
 
+    # Workaround for isinstance calls when importing the field isn't possible
+    _is_jsonfield = True
+
     def __init__(self, *args, **kwargs):
         self.binary = kwargs.pop('binary', False)
         super(JSONField, self).__init__(*args, **kwargs)
diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py
--- a/rest_framework/utils/serializer_helpers.py
+++ b/rest_framework/utils/serializer_helpers.py
@@ -90,7 +90,12 @@ def as_form_field(self):
         # value will be a JSONString, rather than a JSON primitive.
         if not getattr(value, 'is_json_string', False):
             try:
-                value = json.dumps(self.value, sort_keys=True, indent=4)
+                value = json.dumps(
+                    self.value,
+                    sort_keys=True,
+                    indent=4,
+                    separators=(',', ': '),
+                )
             except (TypeError, ValueError):
                 pass
         return self.__class__(self._field, value, self.errors, self._prefix)
@@ -118,6 +123,8 @@ def __getitem__(self, key):
         error = self.errors.get(key) if isinstance(self.errors, dict) else None
         if hasattr(field, 'fields'):
             return NestedBoundField(field, value, error, prefix=self.name + '.')
+        elif getattr(field, '_is_jsonfield', False):
+            return JSONBoundField(field, value, error, prefix=self.name + '.')
         return BoundField(field, value, error, prefix=self.name + '.')
 
     def as_form_field(self):


Test patch:
diff --git a/tests/test_bound_fields.py b/tests/test_bound_fields.py
--- a/tests/test_bound_fields.py
+++ b/tests/test_bound_fields.py
@@ -91,6 +91,10 @@ class ExampleSerializer(serializers.Serializer):
         assert rendered_packed == expected_packed
 
 
+class CustomJSONField(serializers.JSONField):
+    pass
+
+
 class TestNestedBoundField:
     def test_nested_empty_bound_field(self):
         class Nested(serializers.Serializer):
@@ -117,14 +121,31 @@ def test_as_form_fields(self):
         class Nested(serializers.Serializer):
             bool_field = serializers.BooleanField()
             null_field = serializers.IntegerField(allow_null=True)
+            json_field = serializers.JSONField()
+            custom_json_field = CustomJSONField()
 
         class ExampleSerializer(serializers.Serializer):
             nested = Nested()
 
-        serializer = ExampleSerializer(data={'nested': {'bool_field': False, 'null_field': None}})
+        serializer = ExampleSerializer(
+            data={'nested': {
+                'bool_field': False, 'null_field': None,
+                'json_field': {'bool_item': True, 'number': 1, 'text_item': 'text'},
+                'custom_json_field': {'bool_item': True, 'number': 1, 'text_item': 'text'},
+            }})
         assert serializer.is_valid()
         assert serializer['nested']['bool_field'].as_form_field().value == ''
         assert serializer['nested']['null_field'].as_form_field().value == ''
+        assert serializer['nested']['json_field'].as_form_field().value == '''{
+    "bool_item": true,
+    "number": 1,
+    "text_item": "text"
+}'''
+        assert serializer['nested']['custom_json_field'].as_form_field().value == '''{
+    "bool_item": true,
+    "number": 1,
+    "text_item": "text"
+}'''
 
     def test_rendering_nested_fields_with_none_value(self):
         from rest_framework.renderers import HTMLFormRenderer