aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorspiq2011-03-04 12:28:20 +0200
committerspiq2011-03-04 12:28:20 +0200
commit91b33659b562f26665cebc3c7d8aba6d57e9386c (patch)
tree254239bbc2b0c1ffab085e7ac4ae3184aedbe041
parent30fd23d7f5a2d468e39fa8b66bae508df88a4d0d (diff)
downloaddjango-rest-framework-91b33659b562f26665cebc3c7d8aba6d57e9386c.tar.bz2
fix for PUT files
-rw-r--r--djangorestframework/content.py35
-rw-r--r--djangorestframework/parsers.py81
2 files changed, 80 insertions, 36 deletions
diff --git a/djangorestframework/content.py b/djangorestframework/content.py
index d612a2ee..abe2069e 100644
--- a/djangorestframework/content.py
+++ b/djangorestframework/content.py
@@ -24,6 +24,27 @@ class StandardContentMixin(ContentMixin):
return None
return (request.META.get('CONTENT_TYPE', None), request.raw_post_data)
+from django.core.files.base import File
+class SocketFile(File):
+ # Only forward access is allowed
+ def __init__(self, socket, size):
+ super(SocketFile, self).__init__(socket)
+ self._size = int(size)
+ self._pos = 0
+
+ def read(self, num_bytes=None):
+ if num_bytes is None:
+ num_bytes = self._size - self._pos
+ else:
+ num_bytes = min(num_bytes, self._size - self._pos)
+ self._pos += num_bytes
+ return self.file.read(num_bytes)
+
+ def tell(self):
+ return self._pos
+
+ def seek(self, position):
+ pass
class OverloadedContentMixin(ContentMixin):
"""HTTP request content behaviour that also allows arbitrary content to be tunneled in form data."""
@@ -39,7 +60,7 @@ class OverloadedContentMixin(ContentMixin):
Note that content_type may be None if it is unset."""
if not request.META.get('CONTENT_LENGTH', None) and not request.META.get('TRANSFER_ENCODING', None):
return None
-
+
content_type = request.META.get('CONTENT_TYPE', None)
if (request.method == 'POST' and self.CONTENT_PARAM and
@@ -51,5 +72,13 @@ class OverloadedContentMixin(ContentMixin):
content_type = request.POST.get(self.CONTENTTYPE_PARAM, None)
return (content_type, request.POST[self.CONTENT_PARAM])
-
- return (content_type, request.raw_post_data) \ No newline at end of file
+ elif request.method == 'PUT':
+ f = SocketFile(request.environ['wsgi.input'], request.META['CONTENT_LENGTH'])
+ returned = (content_type, f.read())
+ return returned
+ #try:
+ # f.close()
+ #except Exception as e:
+ # print 'exception', e
+ else:
+ return (content_type, request.raw_post_data)
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
index 7c686ca8..e5dd7df4 100644
--- a/djangorestframework/parsers.py
+++ b/djangorestframework/parsers.py
@@ -1,3 +1,7 @@
+from StringIO import StringIO
+
+from django.http.multipartparser import MultiPartParser as DjangoMPParser
+
from djangorestframework.response import ResponseException
from djangorestframework import status
@@ -6,6 +10,10 @@ try:
except ImportError:
import simplejson as json
+try:
+ from urlparse import parse_qs
+except ImportError:
+ from cgi import parse_qs
class ParserMixin(object):
parsers = ()
@@ -75,50 +83,57 @@ class FormParser(BaseParser):
"""The default parser for form data.
Return a dict containing a single value for each non-reserved parameter.
"""
-
+ # TODO: not good, because posted/put lists are flattened !!!
media_type = 'application/x-www-form-urlencoded'
def parse(self, input):
- # The FormParser doesn't parse the input as other parsers would, since Django's already done the
- # form parsing for us. We build the content object from the request directly.
request = self.resource.request
if request.method == 'PUT':
- # Fix from piston to force Django to give PUT requests the same
- # form processing that POST requests get...
- #
- # Bug fix: if _load_post_and_files has already been called, for
- # example by middleware accessing request.POST, the below code to
- # pretend the request is a POST instead of a PUT will be too late
- # to make a difference. Also calling _load_post_and_files will result
- # in the following exception:
- # AttributeError: You cannot set the upload handlers after the upload has been processed.
- # The fix is to check for the presence of the _post field which is set
- # the first time _load_post_and_files is called (both by wsgi.py and
- # modpython.py). If it's set, the request has to be 'reset' to redo
- # the query value parsing in POST mode.
- if hasattr(request, '_post'):
- del request._post
- del request._files
-
- try:
- request.method = "POST"
- request._load_post_and_files()
- request.method = "PUT"
- except AttributeError:
- request.META['REQUEST_METHOD'] = 'POST'
- request._load_post_and_files()
- request.META['REQUEST_METHOD'] = 'PUT'
+ data = parse_qs(input)
+ # Flattening the parsed query data
+ for key, val in data.items():
+ data[key] = val[0]
+
+ if request.method == 'POST':
+ # Django has already done the form parsing for us.
+ data = dict(request.POST.items())
# Strip any parameters that we are treating as reserved
- data = {}
- for (key, val) in request.POST.items():
- if key not in self.resource.RESERVED_FORM_PARAMS:
- data[key] = val
-
+ for key in data:
+ if key in self.resource.RESERVED_FORM_PARAMS:
+ data.pop(key)
return data
# TODO: Allow parsers to specify multiple media_types
class MultipartParser(FormParser):
media_type = 'multipart/form-data'
+ def parse(self, input):
+ request = self.resource.request
+
+ if request.method == 'PUT':
+ upload_handlers = request._get_upload_handlers()
+ django_mpp = DjangoMPParser(request.META, StringIO(input), upload_handlers)
+ data, files = django_mpp.parse()
+ data = dict(data)
+ files = dict(files)
+
+ if request.method == 'POST':
+ # Django has already done the form parsing for us.
+ data = dict(request.POST)
+ files = dict(request.FILES)
+
+ # Flattening, then merging the POSTED/PUT data/files
+ for key, val in dict(data).items():
+ data[key] = val[0]
+ for key, val in dict(files).items():
+ files[key] = val[0].read()
+ data.update(files)
+
+ # Strip any parameters that we are treating as reserved
+ for key in data:
+ if key in self.resource.RESERVED_FORM_PARAMS:
+ data.pop(key)
+ return data
+