diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 8e8c3a9b3..0a3b03729 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -506,6 +506,9 @@ class BrowsableAPIRenderer(BaseRenderer): return self.render_form_for_serializer(serializer) def render_form_for_serializer(self, serializer): + if isinstance(serializer, serializers.ListSerializer): + return None + if hasattr(serializer, 'initial_data'): serializer.is_valid() @@ -555,10 +558,13 @@ class BrowsableAPIRenderer(BaseRenderer): context['indent'] = 4 # strip HiddenField from output + is_list_serializer = isinstance(serializer, serializers.ListSerializer) + serializer = serializer.child if is_list_serializer else serializer data = serializer.data.copy() for name, field in serializer.fields.items(): if isinstance(field, serializers.HiddenField): data.pop(name, None) + data = [data] if is_list_serializer else data content = renderer.render(data, accepted, context) # Renders returns bytes, but CharField expects a str. content = content.decode() diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 71d734c86..247737576 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -633,6 +633,9 @@ class BrowsableAPIRendererTests(URLPatternsTestCase): class AuthExampleViewSet(ExampleViewSet): permission_classes = [permissions.IsAuthenticated] + class SimpleSerializer(serializers.Serializer): + name = serializers.CharField() + router = SimpleRouter() router.register('examples', ExampleViewSet, basename='example') router.register('auth-examples', AuthExampleViewSet, basename='auth-example') @@ -640,6 +643,62 @@ class BrowsableAPIRendererTests(URLPatternsTestCase): def setUp(self): self.renderer = BrowsableAPIRenderer() + self.renderer.accepted_media_type = '' + self.renderer.renderer_context = {} + + def test_render_form_for_serializer(self): + with self.subTest('Serializer'): + serializer = BrowsableAPIRendererTests.SimpleSerializer(data={'name': 'Name'}) + form = self.renderer.render_form_for_serializer(serializer) + assert isinstance(form, str), 'Must return form for serializer' + + with self.subTest('ListSerializer'): + list_serializer = BrowsableAPIRendererTests.SimpleSerializer(data=[{'name': 'Name'}], many=True) + form = self.renderer.render_form_for_serializer(list_serializer) + assert form is None, 'Must not return form for list serializer' + + def test_get_raw_data_form(self): + with self.subTest('Serializer'): + class DummyGenericViewsetLike(APIView): + def get_serializer(self, **kwargs): + return BrowsableAPIRendererTests.SimpleSerializer(**kwargs) + + def get(self, request): + response = Response() + response.view = self + return response + + post = get + + view = DummyGenericViewsetLike.as_view() + _request = APIRequestFactory().get('/') + request = Request(_request) + response = view(_request) + view = response.view + + raw_data_form = self.renderer.get_raw_data_form({'name': 'Name'}, view, 'POST', request) + assert raw_data_form['_content'].initial == '{\n "name": ""\n}' + + with self.subTest('ListSerializer'): + class DummyGenericViewsetLike(APIView): + def get_serializer(self, **kwargs): + return BrowsableAPIRendererTests.SimpleSerializer(many=True, **kwargs) # returns ListSerializer + + def get(self, request): + response = Response() + response.view = self + return response + + post = get + + view = DummyGenericViewsetLike.as_view() + _request = APIRequestFactory().get('/') + request = Request(_request) + response = view(_request) + view = response.view + + raw_data_form = self.renderer.get_raw_data_form([{'name': 'Name'}], view, 'POST', request) + assert raw_data_form['_content'].initial == '[\n {\n "name": ""\n }\n]' def test_get_description_returns_empty_string_for_401_and_403_statuses(self): assert self.renderer.get_description({}, status_code=401) == ''