diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 1062cb32c..4fdd9364d 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -170,7 +170,7 @@ The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`, #### Example -Because `ModelViewSet` extends `GenericAPIView`, you'll normally need to provide at least the `queryset` and `serializer_class` attributes. For example: +Because `ModelViewSet` extends `GenericAPIView`, you'll normally need to provide at least the `queryset` and `serializer_class` attributes, or the `model` attribute shortcut. For example: class AccountViewSet(viewsets.ModelViewSet): """ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index f9ad55f76..e6085f592 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -44,6 +44,7 @@ You can determine your currently installed version using `pip freeze`: * Add in choices information for ChoiceFields in response to `OPTIONS` requests. * Added `pre_delete()` and `post_delete()` method hooks. +* Bugfix: Responses without any content no longer include an HTTP `'Content-Type'` header. * Bugfix: Correctly handle validation errors in PUT-as-create case, responding with 400. ### 2.3.9 diff --git a/rest_framework/response.py b/rest_framework/response.py index 5877c8a3e..1dc6abcf6 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -61,6 +61,10 @@ class Response(SimpleTemplateResponse): assert charset, 'renderer returned unicode, and did not specify ' \ 'a charset value.' return bytes(ret.encode(charset)) + + if not ret: + del self['Content-Type'] + return ret @property diff --git a/rest_framework/tests/test_renderers.py b/rest_framework/tests/test_renderers.py index 76299a890..f7de8fd72 100644 --- a/rest_framework/tests/test_renderers.py +++ b/rest_framework/tests/test_renderers.py @@ -64,11 +64,16 @@ class MockView(APIView): class MockGETView(APIView): - def get(self, request, **kwargs): return Response({'foo': ['bar', 'baz']}) +class EmptyGETView(APIView): + renderer_classes = (JSONRenderer,) + + def get(self, request, **kwargs): + return Response(status=status.HTTP_204_NO_CONTENT) + class HTMLView(APIView): renderer_classes = (BrowsableAPIRenderer, ) @@ -90,6 +95,7 @@ urlpatterns = patterns('', url(r'^jsonp/nojsonrenderer$', MockGETView.as_view(renderer_classes=[JSONPRenderer])), url(r'^html$', HTMLView.as_view()), url(r'^html1$', HTMLView1.as_view()), + url(r'^empty$', EmptyGETView.as_view()), url(r'^api', include('rest_framework.urls', namespace='rest_framework')) ) @@ -219,6 +225,16 @@ class RendererEndToEndTests(TestCase): self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) + def test_204_no_content_responses_have_no_content_type_set(self): + """ + Regression test for #1196 + + https://github.com/tomchristie/django-rest-framework/issues/1196 + """ + resp = self.client.get('/empty') + self.assertEqual(resp.get('Content-Type', None), None) + self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT) + _flat_repr = '{"foo": ["bar", "baz"]}' _indented_repr = '{\n "foo": [\n "bar",\n "baz"\n ]\n}'