aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework/request.py
diff options
context:
space:
mode:
Diffstat (limited to 'djangorestframework/request.py')
-rw-r--r--djangorestframework/request.py159
1 files changed, 84 insertions, 75 deletions
diff --git a/djangorestframework/request.py b/djangorestframework/request.py
index e8f2b8c3..a6c23fb8 100644
--- a/djangorestframework/request.py
+++ b/djangorestframework/request.py
@@ -4,15 +4,14 @@ object received in all the views.
The wrapped request then offers a richer API, in particular :
- - content automatically parsed according to `Content-Type` header, and available as :meth:`.DATA<Request.DATA>`
+ - content automatically parsed according to `Content-Type` header,
+ and available as :meth:`.DATA<Request.DATA>`
- full support of PUT method, including support for file uploads
- form overloading of HTTP method, content type and content
"""
-from djangorestframework.response import ImmediateResponse
from djangorestframework import status
from djangorestframework.utils.mediatypes import is_form_media_type
-from djangorestframework.utils import as_tuple
from StringIO import StringIO
@@ -20,6 +19,14 @@ from StringIO import StringIO
__all__ = ('Request',)
+class Empty:
+ pass
+
+
+def _hasattr(obj, name):
+ return not getattr(obj, name) is Empty
+
+
class Request(object):
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
@@ -35,19 +42,29 @@ class Request(object):
_CONTENT_PARAM = '_content'
def __init__(self, request=None, parsers=None):
- self.request = request
- if parsers is not None:
- self.parsers = parsers
+ self._request = request
+ self.parsers = parsers or ()
+ self._data = Empty
+ self._files = Empty
+ self._method = Empty
+ self._content_type = Empty
+ self._stream = Empty
+
+ def get_parsers(self):
+ """
+ Instantiates and returns the list of parsers the request will use.
+ """
+ return [parser() for parser in self.parsers]
@property
def method(self):
"""
Returns the HTTP method.
- This allows the `method` to be overridden by using a hidden `form` field
- on a form POST request.
+ This allows the `method` to be overridden by using a hidden `form`
+ field on a form POST request.
"""
- if not hasattr(self, '_method'):
+ if not _hasattr(self, '_method'):
self._load_method_and_content_type()
return self._method
@@ -60,11 +77,20 @@ class Request(object):
as it allows the content type to be overridden by using a hidden form
field on a form POST request.
"""
- if not hasattr(self, '_content_type'):
+ if not _hasattr(self, '_content_type'):
self._load_method_and_content_type()
return self._content_type
@property
+ def stream(self):
+ """
+ Returns an object that may be used to stream the request content.
+ """
+ if not _hasattr(self, '_stream'):
+ self._load_stream()
+ return self._stream
+
+ @property
def DATA(self):
"""
Parses the request body and returns the data.
@@ -72,7 +98,7 @@ class Request(object):
Similar to ``request.POST``, except that it handles arbitrary parsers,
and also works on methods other than POST (eg PUT).
"""
- if not hasattr(self, '_data'):
+ if not _hasattr(self, '_data'):
self._load_data_and_files()
return self._data
@@ -83,7 +109,7 @@ class Request(object):
Similar to ``request.FILES``, except that it handles arbitrary parsers,
and also works on methods other than POST (eg PUT).
"""
- if not hasattr(self, '_files'):
+ if not _hasattr(self, '_files'):
self._load_data_and_files()
return self._files
@@ -91,11 +117,11 @@ class Request(object):
"""
Parses the request content into self.DATA and self.FILES.
"""
- if not hasattr(self, '_content_type'):
+ if not _hasattr(self, '_content_type'):
self._load_method_and_content_type()
- if not hasattr(self, '_data'):
- (self._data, self._files) = self._parse(self._get_stream(), self._content_type)
+ if not _hasattr(self, '_data'):
+ (self._data, self._files) = self._parse()
def _load_method_and_content_type(self):
"""
@@ -104,100 +130,83 @@ class Request(object):
self._content_type = self.META.get('HTTP_CONTENT_TYPE', self.META.get('CONTENT_TYPE', ''))
self._perform_form_overloading()
# if the HTTP method was not overloaded, we take the raw HTTP method
- if not hasattr(self, '_method'):
- self._method = self.request.method
-
- def _get_stream(self):
- """
- Returns an object that may be used to stream the request content.
- """
+ if not _hasattr(self, '_method'):
+ self._method = self._request.method
+ def _load_stream(self):
try:
- content_length = int(self.META.get('CONTENT_LENGTH', self.META.get('HTTP_CONTENT_LENGTH')))
+ content_length = int(self.META.get('CONTENT_LENGTH',
+ self.META.get('HTTP_CONTENT_LENGTH')))
except (ValueError, TypeError):
content_length = 0
- # TODO: Add 1.3's LimitedStream to compat and use that.
- # NOTE: Currently only supports parsing request body as a stream with 1.3
if content_length == 0:
- return None
- elif hasattr(self, 'read'):
- return self
- return StringIO(self.raw_post_data)
+ self._stream = None
+ elif hasattr(self._request, 'read'):
+ self._stream = self._request
+ else:
+ self._stream = StringIO(self.raw_post_data)
def _perform_form_overloading(self):
"""
- If this is a form POST request, then we need to check if the method and content/content_type have been
- overridden by setting them in hidden form fields or not.
+ If this is a form POST request, then we need to check if the method and
+ content/content_type have been overridden by setting them in hidden
+ form fields or not.
"""
# We only need to use form overloading on form POST requests.
- if (not self._USE_FORM_OVERLOADING or self.request.method != 'POST'
- or not is_form_media_type(self._content_type)):
+ if (not self._USE_FORM_OVERLOADING
+ or self._request.method != 'POST'
+ or not is_form_media_type(self._content_type)):
return
# At this point we're committed to parsing the request as form data.
- self._data = data = self.POST.copy()
- self._files = self.FILES
+ self._data = self._request.POST
+ self._files = self._request.FILES
# Method overloading - change the method and remove the param from the content.
- if self._METHOD_PARAM in data:
- # NOTE: unlike `get`, `pop` on a `QueryDict` seems to return a list of values.
+ if self._METHOD_PARAM in self._data:
+ # NOTE: `pop` on a `QueryDict` returns a list of values.
self._method = self._data.pop(self._METHOD_PARAM)[0].upper()
# Content overloading - modify the content type, and re-parse.
- if self._CONTENT_PARAM in data and self._CONTENTTYPE_PARAM in data:
+ if (self._CONTENT_PARAM in self._data and
+ self._CONTENTTYPE_PARAM in self._data):
self._content_type = self._data.pop(self._CONTENTTYPE_PARAM)[0]
- stream = StringIO(self._data.pop(self._CONTENT_PARAM)[0])
- (self._data, self._files) = self._parse(stream, self._content_type)
+ self._stream = StringIO(self._data.pop(self._CONTENT_PARAM)[0])
+ (self._data, self._files) = self._parse()
- def _parse(self, stream, content_type):
+ def _parse(self):
"""
Parse the request content.
- May raise a 415 ImmediateResponse (Unsupported Media Type), or a 400 ImmediateResponse (Bad Request).
+ May raise a 415 ImmediateResponse (Unsupported Media Type), or a
+ 400 ImmediateResponse (Bad Request).
"""
- if stream is None or content_type is None:
+ if self.stream is None or self.content_type is None:
return (None, None)
- for parser in as_tuple(self.parsers):
- if parser.can_handle_request(content_type):
- return parser.parse(stream)
+ for parser in self.get_parsers():
+ if parser.can_handle_request(self.content_type):
+ return parser.parse(self.stream, self.META, self.upload_handlers)
- raise ImmediateResponse({
- 'error': 'Unsupported media type in request \'%s\'.' % content_type},
- status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
+ self._raise_415_response(self._content_type)
- @property
- def _parsed_media_types(self):
+ def _raise_415_response(self, content_type):
"""
- Return a list of all the media types that this view can parse.
+ Raise a 415 response if we cannot parse the given content type.
"""
- return [parser.media_type for parser in self.parsers]
+ from djangorestframework.response import ImmediateResponse
- @property
- def _default_parser(self):
- """
- Return the view's default parser class.
- """
- return self.parsers[0]
-
- def _get_parsers(self):
- if hasattr(self, '_parsers'):
- return self._parsers
- return ()
-
- def _set_parsers(self, value):
- self._parsers = value
-
- parsers = property(_get_parsers, _set_parsers)
+ raise ImmediateResponse(
+ {
+ 'error': 'Unsupported media type in request \'%s\'.'
+ % content_type
+ },
+ status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
def __getattr__(self, name):
"""
- When an attribute is not present on the calling instance, try to get it
- from the original request.
+ Proxy other attributes to the underlying HttpRequest object.
"""
- if hasattr(self.request, name):
- return getattr(self.request, name)
- else:
- return super(Request, self).__getattribute__(name)
+ return getattr(self._request, name)