From 11d8e4df88d6bd257379822f220e34ad3154f504 Mon Sep 17 00:00:00 2001 From: Dave Allan Date: Sat, 1 Apr 2017 20:31:27 -0400 Subject: [PATCH 1/3] PoC Add JSONBoundField to serializers (Fixes #4999) Per issue #4999, JSONFields are not rendered properly in the DRF browsable API HTML forms. This patch attempts to fix that behavior by introducing a JSONBoundField helper similar to the NestedBoundField helper. --- rest_framework/serializers.py | 5 ++++- rest_framework/utils/serializer_helpers.py | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index ba01c3434..6e7e76fa8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -38,7 +38,8 @@ from rest_framework.utils.field_mapping import ( get_relation_kwargs, get_url_kwargs ) from rest_framework.utils.serializer_helpers import ( - BindingDict, BoundField, NestedBoundField, ReturnDict, ReturnList + BindingDict, BoundField, JSONBoundField, NestedBoundField, ReturnDict, + ReturnList ) from rest_framework.validators import ( UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator, @@ -521,6 +522,8 @@ class Serializer(BaseSerializer): error = self.errors.get(key) if hasattr(self, '_errors') else None if isinstance(field, Serializer): return NestedBoundField(field, value, error) + if isinstance(field, JSONField): + return JSONBoundField(field, value, error) return BoundField(field, value, error) # Include a backlink to the serializer class on return objects. diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 4734332af..67b89634c 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import collections +import json from collections import OrderedDict from django.utils.encoding import force_text @@ -82,6 +83,12 @@ class BoundField(object): return self.__class__(self._field, value, self.errors, self._prefix) +class JSONBoundField(BoundField): + def as_form_field(self): + value = json.dumps(self.value) + return self.__class__(self._field, value, self.errors, self._prefix) + + class NestedBoundField(BoundField): """ This `BoundField` additionally implements __iter__ and __getitem__ From 7a0bd1c4d1950f49d8e0710f535d3d15c2c43d79 Mon Sep 17 00:00:00 2001 From: Dave Allan Date: Wed, 26 Apr 2017 12:00:32 -0400 Subject: [PATCH 2/3] Use text area for JSON fields and indent JSON --- rest_framework/utils/field_mapping.py | 4 ++-- rest_framework/utils/serializer_helpers.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index b8817d976..cc82492dc 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -8,7 +8,7 @@ from django.core import validators from django.db import models from django.utils.text import capfirst -from rest_framework.compat import DecimalValidator +from rest_framework.compat import DecimalValidator, JSONField from rest_framework.validators import UniqueValidator NUMERIC_FIELD_TYPES = ( @@ -88,7 +88,7 @@ def get_field_kwargs(field_name, model_field): if decimal_places is not None: kwargs['decimal_places'] = decimal_places - if isinstance(model_field, models.TextField): + if isinstance(model_field, models.TextField) or (JSONField and isinstance(model_field, JSONField)): kwargs['style'] = {'base_template': 'textarea.html'} if isinstance(model_field, models.AutoField) or not model_field.editable: diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 67b89634c..2c4407d74 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -85,7 +85,7 @@ class BoundField(object): class JSONBoundField(BoundField): def as_form_field(self): - value = json.dumps(self.value) + value = json.dumps(self.value, sort_keys=True, indent=4) return self.__class__(self._field, value, self.errors, self._prefix) From f56b1170643021b71eff5dba8f5e61f4de322b74 Mon Sep 17 00:00:00 2001 From: Dave Allan Date: Wed, 26 Apr 2017 13:48:49 -0400 Subject: [PATCH 3/3] Pass non-JSON serializable values through unmodified --- rest_framework/utils/serializer_helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 2c4407d74..d4c659e5f 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -85,7 +85,11 @@ class BoundField(object): class JSONBoundField(BoundField): def as_form_field(self): - value = json.dumps(self.value, sort_keys=True, indent=4) + value = self.value + try: + value = json.dumps(self.value, sort_keys=True, indent=4) + except TypeError: + pass return self.__class__(self._field, value, self.errors, self._prefix)