mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
Fallback behavior for request parsing when request.POST already accessed. (#4500)
This commit is contained in:
parent
e82ee91078
commit
be74d11165
|
@ -15,6 +15,7 @@ import sys
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.http.multipartparser import parse_header
|
from django.http.multipartparser import parse_header
|
||||||
|
from django.http.request import RawPostDataException
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
|
||||||
|
@ -263,10 +264,20 @@ class Request(object):
|
||||||
|
|
||||||
if content_length == 0:
|
if content_length == 0:
|
||||||
self._stream = None
|
self._stream = None
|
||||||
elif hasattr(self._request, 'read'):
|
elif not self._request._read_started:
|
||||||
self._stream = self._request
|
self._stream = self._request
|
||||||
else:
|
else:
|
||||||
self._stream = six.BytesIO(self.raw_post_data)
|
self._stream = six.BytesIO(self.body)
|
||||||
|
|
||||||
|
def _supports_form_parsing(self):
|
||||||
|
"""
|
||||||
|
Return True if this requests supports parsing form data.
|
||||||
|
"""
|
||||||
|
form_media = (
|
||||||
|
'application/x-www-form-urlencoded',
|
||||||
|
'multipart/form-data'
|
||||||
|
)
|
||||||
|
return any([parser.media_type in form_media for parser in self.parsers])
|
||||||
|
|
||||||
def _parse(self):
|
def _parse(self):
|
||||||
"""
|
"""
|
||||||
|
@ -274,8 +285,18 @@ class Request(object):
|
||||||
|
|
||||||
May raise an `UnsupportedMediaType`, or `ParseError` exception.
|
May raise an `UnsupportedMediaType`, or `ParseError` exception.
|
||||||
"""
|
"""
|
||||||
stream = self.stream
|
|
||||||
media_type = self.content_type
|
media_type = self.content_type
|
||||||
|
try:
|
||||||
|
stream = self.stream
|
||||||
|
except RawPostDataException:
|
||||||
|
if not hasattr(self._request, '_post'):
|
||||||
|
raise
|
||||||
|
# If request.POST has been accessed in middleware, and a method='POST'
|
||||||
|
# request was made with 'multipart/form-data', then the request stream
|
||||||
|
# will already have been exhausted.
|
||||||
|
if self._supports_form_parsing():
|
||||||
|
return (self._request.POST, self._request.FILES)
|
||||||
|
stream = None
|
||||||
|
|
||||||
if stream is None or media_type is None:
|
if stream is None or media_type is None:
|
||||||
empty_data = QueryDict('', encoding=self._request._encoding)
|
empty_data = QueryDict('', encoding=self._request._encoding)
|
||||||
|
|
|
@ -7,11 +7,16 @@ from django import forms
|
||||||
from django.core.files.uploadhandler import (
|
from django.core.files.uploadhandler import (
|
||||||
MemoryFileUploadHandler, TemporaryFileUploadHandler
|
MemoryFileUploadHandler, TemporaryFileUploadHandler
|
||||||
)
|
)
|
||||||
|
from django.http.request import RawPostDataException
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.six.moves import StringIO
|
from django.utils.six.moves import StringIO
|
||||||
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.parsers import FileUploadParser, FormParser
|
from rest_framework.parsers import (
|
||||||
|
FileUploadParser, FormParser, JSONParser, MultiPartParser
|
||||||
|
)
|
||||||
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.test import APIRequestFactory
|
||||||
|
|
||||||
|
|
||||||
class Form(forms.Form):
|
class Form(forms.Form):
|
||||||
|
@ -122,3 +127,39 @@ class TestFileUploadParser(TestCase):
|
||||||
|
|
||||||
def __replace_content_disposition(self, disposition):
|
def __replace_content_disposition(self, disposition):
|
||||||
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
|
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
|
||||||
|
|
||||||
|
|
||||||
|
class TestPOSTAccessed(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.factory = APIRequestFactory()
|
||||||
|
|
||||||
|
def test_post_accessed_in_post_method(self):
|
||||||
|
django_request = self.factory.post('/', {'foo': 'bar'})
|
||||||
|
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
|
||||||
|
django_request.POST
|
||||||
|
assert request.POST == {'foo': ['bar']}
|
||||||
|
assert request.data == {'foo': ['bar']}
|
||||||
|
|
||||||
|
def test_post_accessed_in_post_method_with_json_parser(self):
|
||||||
|
django_request = self.factory.post('/', {'foo': 'bar'})
|
||||||
|
request = Request(django_request, parsers=[JSONParser()])
|
||||||
|
django_request.POST
|
||||||
|
assert request.POST == {}
|
||||||
|
assert request.data == {}
|
||||||
|
|
||||||
|
def test_post_accessed_in_put_method(self):
|
||||||
|
django_request = self.factory.put('/', {'foo': 'bar'})
|
||||||
|
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
|
||||||
|
django_request.POST
|
||||||
|
assert request.POST == {'foo': ['bar']}
|
||||||
|
assert request.data == {'foo': ['bar']}
|
||||||
|
|
||||||
|
def test_request_read_before_parsing(self):
|
||||||
|
django_request = self.factory.put('/', {'foo': 'bar'})
|
||||||
|
request = Request(django_request, parsers=[FormParser(), MultiPartParser()])
|
||||||
|
django_request.read()
|
||||||
|
with pytest.raises(RawPostDataException):
|
||||||
|
request.POST
|
||||||
|
with pytest.raises(RawPostDataException):
|
||||||
|
request.POST
|
||||||
|
request.data
|
||||||
|
|
Loading…
Reference in New Issue
Block a user