Add docs for 'Request.http_request`

- Add docs for accessing the underlying 'HttpRequest' object, and warn
  users that doing so is considered to be advanced, non-standard usage.
- Add tests about duplicate stream parsing assumptions, which warrant
  the above warning. These tests are currently failing due to a bug.
This commit is contained in:
Ryan P Kilby 2018-01-25 06:35:18 -05:00
parent 9bb17e4df6
commit 335ba0e377
2 changed files with 50 additions and 0 deletions

View File

@ -130,6 +130,9 @@ As REST framework's `Request` extends Django's `HttpRequest`, all the other stan
Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition. Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition.
# Accessing the HttpRequest
The underlying `HttpRequest` can be accessed through the `.http_request` attribute. While direct manipulation of the `HttpRequest` is discouraged, there are some advanced use cases that may require it. For example, one view may delegate request handling to a secondary view function. In this case, it is necessary to pass the original `HttpRequest` to the delegated view instead of the DRF `Request` object. Be aware that duplicate processing of the `HttpRequest` may have have unintended side effects. For example, if the request stream has already been consumed, it may not be accessible for a second read and will raise an exception.
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
[parsers documentation]: parsers.md [parsers documentation]: parsers.md

View File

@ -13,6 +13,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sessions.middleware import SessionMiddleware
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.http.request import RawPostDataException
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six from django.utils import six
@ -137,6 +138,11 @@ class MockView(APIView):
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class EchoView(APIView):
def post(self, request):
return Response(status=status.HTTP_200_OK, data=request.data)
class FileUploadView(APIView): class FileUploadView(APIView):
def post(self, request): def post(self, request):
filenames = [file.temporary_file_path() for file in request.FILES.values()] filenames = [file.temporary_file_path() for file in request.FILES.values()]
@ -149,6 +155,7 @@ class FileUploadView(APIView):
urlpatterns = [ urlpatterns = [
url(r'^$', MockView.as_view()), url(r'^$', MockView.as_view()),
url(r'^echo/$', EchoView.as_view()),
url(r'^upload/$', FileUploadView.as_view()) url(r'^upload/$', FileUploadView.as_view())
] ]
@ -311,3 +318,43 @@ class TestHttpRequest(TestCase):
"`_request` has been deprecated in favor of " "`_request` has been deprecated in favor of "
"`http_request`, and will be removed in 3.10" "`http_request`, and will be removed in 3.10"
) )
@override_settings(ROOT_URLCONF='tests.test_request')
def test_duplicate_request_stream_parsing_exception(self):
"""
Check assumption that duplicate stream parsing will result in a
`RawPostDataException` being raised.
"""
response = APIClient().post('/echo/', data={'a': 'b'}, format='json')
request = response.renderer_context['request']
# ensure that request stream was consumed by json parser
assert request.content_type.startswith('application/json')
assert response.data == {'a': 'b'}
# pass same HttpRequest to view, stream already consumed
with pytest.raises(RawPostDataException):
EchoView.as_view()(request.http_request)
@override_settings(ROOT_URLCONF='tests.test_request')
def test_duplicate_request_form_data_access(self):
"""
Form data is copied to the underlying django request for middleware
and file closing reasons. Duplicate processing of a request with form
data is 'safe' in so far as accessing `request.POST` does not trigger
the duplicate stream parse exception.
"""
response = APIClient().post('/echo/', data={'a': 'b'})
request = response.renderer_context['request']
# ensure that request stream was consumed by form parser
assert request.content_type.startswith('multipart/form-data')
assert response.data == {'a': ['b']}
# pass same HttpRequest to view, form data set on underlying request
response = EchoView.as_view()(request.http_request)
request = response.renderer_context['request']
# ensure that request stream was consumed by form parser
assert request.content_type.startswith('multipart/form-data')
assert response.data == {'a': ['b']}