aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebpiq2011-03-11 14:34:39 +0200
committersebpiq2011-03-11 14:34:39 +0200
commitd6c13a9e5caee653f7a8f3150c00aa574aa15700 (patch)
tree77cfb477b5662677964e74a3a4fe27d687e3006e
parent94199a484783d91317c8decb273ab60447ffdfd7 (diff)
downloaddjango-rest-framework-d6c13a9e5caee653f7a8f3150c00aa574aa15700.tar.bz2
documentation + tests + debugging for formparsers
-rw-r--r--djangorestframework/content.py2
-rw-r--r--djangorestframework/parsers.py30
-rw-r--r--djangorestframework/tests/__init__.py4
-rw-r--r--djangorestframework/tests/parsers.py72
4 files changed, 90 insertions, 18 deletions
diff --git a/djangorestframework/content.py b/djangorestframework/content.py
index f9a3c6de..cfdd33be 100644
--- a/djangorestframework/content.py
+++ b/djangorestframework/content.py
@@ -50,7 +50,7 @@ class OverloadedContentMixin(ContentMixin):
content_type = None
if self.CONTENTTYPE_PARAM and request.POST.get(self.CONTENTTYPE_PARAM, None):
content_type = request.POST.get(self.CONTENTTYPE_PARAM, None)
- request.META['CONTENT_TYPE'] = content_type
+ request.META['CONTENT_TYPE'] = content_type # TODO : VERY BAD, avoid modifying original request.
return (content_type, request.POST[self.CONTENT_PARAM])
else:
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
index 081fa689..d324f687 100644
--- a/djangorestframework/parsers.py
+++ b/djangorestframework/parsers.py
@@ -84,23 +84,23 @@ class DataFlatener(object):
def flatten_data(self, data):
"""Given a data dictionary {<key>: <value_list>}, returns a flattened dictionary
with information provided by the method "is_a_list"."""
+ data = data.copy()
flatdata = dict()
- for key, attr_value in data.items():
- if self.is_a_list(key):
- if isinstance(attr_value, list):
- flatdata[key] = attr_value
- else:
- flatdata[key] = [attr_value]
+ for key, val_list in data.items():
+ if self.is_a_list(key, val_list):
+ flatdata[key] = val_list
else:
- if isinstance(attr_value, list):
- flatdata[key] = attr_value[0]
+ if val_list:
+ flatdata[key] = val_list[0]
else:
- flatdata[key] = attr_value
+ # If the list is empty, but the parameter is not a list,
+ # we strip this parameter.
+ data.pop(key)
return flatdata
- def is_a_list(self, key, val):
+ def is_a_list(self, key, val_list):
"""Returns True if the parameter with name *key* is expected to be a list, or False otherwise.
- *val* which is the received value for parameter *key* can be used to guess the answer."""
+ *val_list* which is the received value for parameter *key* can be used to guess the answer."""
return False
class FormParser(BaseParser, DataFlatener):
@@ -121,12 +121,12 @@ class FormParser(BaseParser, DataFlatener):
EMPTY_VALUE = '_empty'
def parse(self, input):
- data = parse_qs(input)
+ data = parse_qs(input, keep_blank_values=True)
- # Flatening data and removing EMPTY_VALUEs from the lists
+ # removing EMPTY_VALUEs from the lists and flatening the data
+ for key, val_list in data.items():
+ self.remove_empty_val(val_list)
data = self.flatten_data(data)
- for key in filter(lambda k: self.is_a_list(k), data):
- self.remove_empty_val(data[key])
# Strip any parameters that we are treating as reserved
for key in data.keys():
diff --git a/djangorestframework/tests/__init__.py b/djangorestframework/tests/__init__.py
index 55e386b5..5d5b652a 100644
--- a/djangorestframework/tests/__init__.py
+++ b/djangorestframework/tests/__init__.py
@@ -10,5 +10,5 @@ __test__ = dict()
for module in modules:
exec("from djangorestframework.tests.%s import __doc__ as module_doc" % module)
exec("from djangorestframework.tests.%s import *" % module)
- __test__['%s' % module] = module_doc or ""
-
+ __test__[module] = module_doc or ""
+
diff --git a/djangorestframework/tests/parsers.py b/djangorestframework/tests/parsers.py
new file mode 100644
index 00000000..a2831b63
--- /dev/null
+++ b/djangorestframework/tests/parsers.py
@@ -0,0 +1,72 @@
+"""
+..
+ >>> from djangorestframework.parsers import FormParser
+ >>> from djangorestframework.resource import Resource
+ >>> from djangorestframework.compat import RequestFactory
+ >>> from urllib import urlencode
+ >>> req = RequestFactory().get('/')
+ >>> some_resource = Resource()
+ >>> trash = some_resource.dispatch(req)# Some variables are set only when calling dispatch
+
+Data flatening
+----------------
+
+Here is some example data, which would eventually be sent along with a post request :
+
+ >>> inpt = urlencode([
+ ... ('key1', 'bla1'),
+ ... ('key2', 'blo1'), ('key2', 'blo2'),
+ ... ])
+
+Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
+
+ >>> FormParser(some_resource).parse(inpt) == {'key1': 'bla1', 'key2': 'blo1'}
+ True
+
+However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
+
+ >>> class MyFormParser(FormParser):
+ ...
+ ... def is_a_list(self, key, val_list):
+ ... return len(val_list) > 1
+
+This new parser only flattens the lists of parameters that contain a single value.
+
+ >>> MyFormParser(some_resource).parse(inpt) == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
+ True
+
+Submitting an empty list
+--------------------------
+
+When submitting an empty select multiple, like this one ::
+
+ <select multiple="multiple" name="key2"></select>
+
+The browsers usually strip the parameter completely. A hack to avoid this, and therefore being able to submit an empty select multiple, is to submit a value that tells the server that the list is empty ::
+
+ <select multiple="multiple" name="key2"><option value="_empty"></select>
+
+:class:`parsers.FormParser` provides the server-side implementation for this hack. Considering the following posted data :
+
+ >>> inpt = urlencode([
+ ... ('key1', 'blo1'), ('key1', '_empty'),
+ ... ('key2', '_empty'),
+ ... ])
+
+:class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
+
+ >>> MyFormParser(some_resource).parse(inpt) == {'key1': 'blo1'}
+ True
+
+Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
+
+ >>> class MyFormParser(FormParser):
+ ...
+ ... def is_a_list(self, key, val_list):
+ ... return key == 'key2'
+ ...
+ >>> MyFormParser(some_resource).parse(inpt) == {'key1': 'blo1', 'key2': []}
+ True
+
+Better like that. Note also that you can configure something else than ``_empty`` for the empty value by setting :class:`parsers.FormParser.EMPTY_VALUE`.
+"""