mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-10 19:56:59 +03:00
Render JSON fields with proper indentation in browsable API forms. (#6243)
* Fix JSONBoundField usage on nested serializers (#6211) * Unify JSONBoundField as_form_field output between py2 and py3 When using json.dumps with indenting, in python2 the default formatting prints whitespace after commas (,) and python3 does not. This can be unified with the separators keyword argument.
This commit is contained in:
parent
ff625ecff5
commit
b256c46cb1
|
@ -1764,6 +1764,9 @@ class JSONField(Field):
|
||||||
'invalid': _('Value must be valid JSON.')
|
'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):
|
def __init__(self, *args, **kwargs):
|
||||||
self.binary = kwargs.pop('binary', False)
|
self.binary = kwargs.pop('binary', False)
|
||||||
self.encoder = kwargs.pop('encoder', None)
|
self.encoder = kwargs.pop('encoder', None)
|
||||||
|
|
|
@ -87,7 +87,12 @@ class JSONBoundField(BoundField):
|
||||||
# value will be a JSONString, rather than a JSON primitive.
|
# value will be a JSONString, rather than a JSON primitive.
|
||||||
if not getattr(value, 'is_json_string', False):
|
if not getattr(value, 'is_json_string', False):
|
||||||
try:
|
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):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
return self.__class__(self._field, value, self.errors, self._prefix)
|
return self.__class__(self._field, value, self.errors, self._prefix)
|
||||||
|
@ -115,6 +120,8 @@ class NestedBoundField(BoundField):
|
||||||
error = self.errors.get(key) if isinstance(self.errors, dict) else None
|
error = self.errors.get(key) if isinstance(self.errors, dict) else None
|
||||||
if hasattr(field, 'fields'):
|
if hasattr(field, 'fields'):
|
||||||
return NestedBoundField(field, value, error, prefix=self.name + '.')
|
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 + '.')
|
return BoundField(field, value, error, prefix=self.name + '.')
|
||||||
|
|
||||||
def as_form_field(self):
|
def as_form_field(self):
|
||||||
|
|
|
@ -91,6 +91,10 @@ class TestSimpleBoundField:
|
||||||
assert rendered_packed == expected_packed
|
assert rendered_packed == expected_packed
|
||||||
|
|
||||||
|
|
||||||
|
class CustomJSONField(serializers.JSONField):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestNestedBoundField:
|
class TestNestedBoundField:
|
||||||
def test_nested_empty_bound_field(self):
|
def test_nested_empty_bound_field(self):
|
||||||
class Nested(serializers.Serializer):
|
class Nested(serializers.Serializer):
|
||||||
|
@ -117,14 +121,31 @@ class TestNestedBoundField:
|
||||||
class Nested(serializers.Serializer):
|
class Nested(serializers.Serializer):
|
||||||
bool_field = serializers.BooleanField()
|
bool_field = serializers.BooleanField()
|
||||||
null_field = serializers.IntegerField(allow_null=True)
|
null_field = serializers.IntegerField(allow_null=True)
|
||||||
|
json_field = serializers.JSONField()
|
||||||
|
custom_json_field = CustomJSONField()
|
||||||
|
|
||||||
class ExampleSerializer(serializers.Serializer):
|
class ExampleSerializer(serializers.Serializer):
|
||||||
nested = Nested()
|
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.is_valid()
|
||||||
assert serializer['nested']['bool_field'].as_form_field().value == ''
|
assert serializer['nested']['bool_field'].as_form_field().value == ''
|
||||||
assert serializer['nested']['null_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):
|
def test_rendering_nested_fields_with_none_value(self):
|
||||||
from rest_framework.renderers import HTMLFormRenderer
|
from rest_framework.renderers import HTMLFormRenderer
|
||||||
|
|
Loading…
Reference in New Issue
Block a user