mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	documentation + tests + debugging for formparsers
This commit is contained in:
		
							parent
							
								
									94199a4847
								
							
						
					
					
						commit
						d6c13a9e5c
					
				| 
						 | 
					@ -50,7 +50,7 @@ class OverloadedContentMixin(ContentMixin):
 | 
				
			||||||
            content_type = None
 | 
					            content_type = None
 | 
				
			||||||
            if self.CONTENTTYPE_PARAM and request.POST.get(self.CONTENTTYPE_PARAM, None):
 | 
					            if self.CONTENTTYPE_PARAM and request.POST.get(self.CONTENTTYPE_PARAM, None):
 | 
				
			||||||
                content_type = 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])
 | 
					            return (content_type, request.POST[self.CONTENT_PARAM])
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,23 +84,23 @@ class DataFlatener(object):
 | 
				
			||||||
    def flatten_data(self, data):
 | 
					    def flatten_data(self, data):
 | 
				
			||||||
        """Given a data dictionary {<key>: <value_list>}, returns a flattened dictionary
 | 
					        """Given a data dictionary {<key>: <value_list>}, returns a flattened dictionary
 | 
				
			||||||
        with information provided by the method "is_a_list"."""
 | 
					        with information provided by the method "is_a_list"."""
 | 
				
			||||||
 | 
					        data = data.copy()
 | 
				
			||||||
        flatdata = dict()
 | 
					        flatdata = dict()
 | 
				
			||||||
        for key, attr_value in data.items():
 | 
					        for key, val_list in data.items():
 | 
				
			||||||
            if self.is_a_list(key):
 | 
					            if self.is_a_list(key, val_list):
 | 
				
			||||||
                if isinstance(attr_value, list):
 | 
					                flatdata[key] = val_list
 | 
				
			||||||
                    flatdata[key] = attr_value
 | 
					 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                    flatdata[key] = [attr_value]
 | 
					                if val_list:
 | 
				
			||||||
 | 
					                    flatdata[key] = val_list[0]
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                if isinstance(attr_value, list):
 | 
					                    # If the list is empty, but the parameter is not a list,
 | 
				
			||||||
                    flatdata[key] = attr_value[0]
 | 
					                    # we strip this parameter.
 | 
				
			||||||
                else:
 | 
					                    data.pop(key)
 | 
				
			||||||
                    flatdata[key] = attr_value 
 | 
					 | 
				
			||||||
        return flatdata
 | 
					        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.
 | 
					        """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
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FormParser(BaseParser, DataFlatener):
 | 
					class FormParser(BaseParser, DataFlatener):
 | 
				
			||||||
| 
						 | 
					@ -121,12 +121,12 @@ class FormParser(BaseParser, DataFlatener):
 | 
				
			||||||
    EMPTY_VALUE = '_empty'
 | 
					    EMPTY_VALUE = '_empty'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self, input):
 | 
					    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)
 | 
					        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
 | 
					        # Strip any parameters that we are treating as reserved
 | 
				
			||||||
        for key in data.keys():
 | 
					        for key in data.keys():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,5 +10,5 @@ __test__ = dict()
 | 
				
			||||||
for module in modules:
 | 
					for module in modules:
 | 
				
			||||||
    exec("from djangorestframework.tests.%s import __doc__ as module_doc" % module)
 | 
					    exec("from djangorestframework.tests.%s import __doc__ as module_doc" % module)
 | 
				
			||||||
    exec("from djangorestframework.tests.%s import *" % module)
 | 
					    exec("from djangorestframework.tests.%s import *" % module)
 | 
				
			||||||
    __test__['%s' % module] = module_doc or ""
 | 
					    __test__[module] = module_doc or ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										72
									
								
								djangorestframework/tests/parsers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								djangorestframework/tests/parsers.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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`.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user