From daba5e9ba5cea03d56e76baa4bf05d67b7ac6cea Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Mon, 4 Dec 2017 02:39:55 -0500 Subject: [PATCH] Fix Serializer.data when provided invalid 'data' (#5646) * Test serializer/API renderer for invalid datatype * Fix Serializer.data with invalid input datatype --- rest_framework/serializers.py | 4 +++ tests/browsable_api/test_form_rendering.py | 30 ++++++++++++++++++++++ tests/test_serializer.py | 9 +++++++ 3 files changed, 43 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index fa6a6f0bb..e2ea0d744 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -398,6 +398,10 @@ class Serializer(BaseSerializer): def get_initial(self): if hasattr(self, 'initial_data'): + # initial_data may not be a valid type + if not isinstance(self.initial_data, Mapping): + return OrderedDict() + return OrderedDict([ (field_name, field.get_value(self.initial_data)) for field_name, field in self.fields.items() diff --git a/tests/browsable_api/test_form_rendering.py b/tests/browsable_api/test_form_rendering.py index 47186c783..d8378a2ca 100644 --- a/tests/browsable_api/test_form_rendering.py +++ b/tests/browsable_api/test_form_rendering.py @@ -14,6 +14,10 @@ class BasicSerializer(serializers.ModelSerializer): fields = '__all__' +class StandardPostView(generics.CreateAPIView): + serializer_class = BasicSerializer + + class ManyPostView(generics.GenericAPIView): queryset = BasicModel.objects.all() serializer_class = BasicSerializer @@ -24,6 +28,32 @@ class ManyPostView(generics.GenericAPIView): return Response(serializer.data, status.HTTP_200_OK) +class TestPostingListData(TestCase): + """ + POSTing a list of data to a regular view should not cause the browsable + API to fail during rendering. + + Regression test for https://github.com/encode/django-rest-framework/issues/5637 + """ + + def test_json_response(self): + # sanity check for non-browsable API responses + view = StandardPostView.as_view() + request = factory.post('/', [{}], format='json') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertTrue('non_field_errors' in response.data) + + def test_browsable_api(self): + view = StandardPostView.as_view() + request = factory.post('/?format=api', [{}], format='json') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertTrue('non_field_errors' in response.data) + + class TestManyPostView(TestCase): def setUp(self): """ diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0c30deb91..da1394515 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -77,14 +77,23 @@ class TestSerializer: serializer = self.Serializer(data={'char': 'abc', 'integer': 123}) assert serializer.is_valid() assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.data == {'char': 'abc', 'integer': 123} assert serializer.errors == {} def test_invalid_serializer(self): serializer = self.Serializer(data={'char': 'abc'}) assert not serializer.is_valid() assert serializer.validated_data == {} + assert serializer.data == {'char': 'abc'} assert serializer.errors == {'integer': ['This field is required.']} + def test_invalid_datatype(self): + serializer = self.Serializer(data=[{'char': 'abc'}]) + assert not serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.data == {} + assert serializer.errors == {'non_field_errors': ['Invalid data. Expected a dictionary, but got list.']} + def test_partial_validation(self): serializer = self.Serializer(data={'char': 'abc'}, partial=True) assert serializer.is_valid()