Moving of nested form tokenization into parsers, plus some minor function naming changes

This commit is contained in:
Dan Stephenson 2013-09-06 00:43:29 +01:00
parent 53e7b9fafc
commit 4e0b3ad1b7
3 changed files with 22 additions and 12 deletions

View File

@ -13,6 +13,8 @@ from django.http.multipartparser import MultiPartParserError, parse_header, Chun
from rest_framework.compat import yaml, etree from rest_framework.compat import yaml, etree
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.compat import six from rest_framework.compat import six
from rest_framework.utils.datastructures import TokenExpandedDict
from rest_framework.settings import api_settings
import json import json
import datetime import datetime
import decimal import decimal
@ -40,6 +42,16 @@ class BaseParser(object):
""" """
raise NotImplementedError(".parse() must be overridden.") raise NotImplementedError(".parse() must be overridden.")
def _parse_tokenization(self, data):
"""
Configuration dependant processing of input data, where character tokens
(such as periods '.') can be used to reshape for data into nested form,
for use with NestedModelSerializer.
"""
if api_settings.NESTED_FIELDS:
data = TokenExpandedDict(data)
return data
class JSONParser(BaseParser): class JSONParser(BaseParser):
""" """
@ -108,6 +120,8 @@ class FormParser(BaseParser):
parser_context = parser_context or {} parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
data = QueryDict(stream.read(), encoding=encoding) data = QueryDict(stream.read(), encoding=encoding)
data = self._parse_tokenization(data)
return data return data
@ -134,6 +148,8 @@ class MultiPartParser(BaseParser):
try: try:
parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding) parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
data, files = parser.parse() data, files = parser.parse()
data = self._parse_tokenization(data)
return DataAndFiles(data, files) return DataAndFiles(data, files)
except MultiPartParserError as exc: except MultiPartParserError as exc:
raise ParseError('Multipart form parse error - %s' % six.u(exc)) raise ParseError('Multipart form parse error - %s' % six.u(exc))

View File

@ -13,7 +13,6 @@ 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.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from rest_framework.utils.datastructures import DotExpandedDict
from rest_framework import HTTP_HEADER_ENCODING from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework.compat import BytesIO from rest_framework.compat import BytesIO
@ -151,12 +150,8 @@ class Request(object):
Similar to usual behaviour of `request.POST`, except that it handles Similar to usual behaviour of `request.POST`, except that it handles
arbitrary parsers, and also works on methods other than POST (eg PUT). 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() # self._load_data_and_files()
if api_settings.NESTED_FIELDS:
self._data = DotExpandedDict(self._data)
return self._data return self._data
@property @property
@ -282,8 +277,7 @@ class Request(object):
return return
# At this point we're committed to parsing the request as form data. # At this point we're committed to parsing the request as form data.
self._data = self._request.POST self._data, self._files = self._parse()
self._files = self._request.FILES
# Method overloading - change the method and remove the param from the content. # Method overloading - change the method and remove the param from the content.
if (self._METHOD_PARAM and if (self._METHOD_PARAM and

View File

@ -4,13 +4,13 @@ Utility functions for reshaping datastructures
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from django.http import QueryDict from django.http import QueryDict
class DotExpandedDict(QueryDict): class TokenExpandedDict(QueryDict):
""" """
A special dictionary constructor that takes a dictionary in which the keys A special dictionary constructor that takes a dictionary in which the keys
may contain dots to specify inner dictionaries. It's confusing, but this may contain dots to specify inner dictionaries. It's confusing, but this
example should make sense. example should make sense.
>>> d = DotExpandedDict({'person.1.firstname': ['Simon'], \ >>> d = TokenExpandedDict({'person.1.firstname': ['Simon'], \
'person.1.lastname': ['Willison'], \ 'person.1.lastname': ['Willison'], \
'person.2.firstname': ['Adrian'], \ 'person.2.firstname': ['Adrian'], \
'person.2.lastname': ['Holovaty']}) 'person.2.lastname': ['Holovaty']})
@ -22,7 +22,7 @@ class DotExpandedDict(QueryDict):
{'lastname': ['Willison'], 'firstname': ['Simon']} {'lastname': ['Willison'], 'firstname': ['Simon']}
# Gotcha: Results are unpredictable if the dots are "uneven": # Gotcha: Results are unpredictable if the dots are "uneven":
>>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1}) >>> TokenExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
{'c': 1} {'c': 1}
""" """
def __init__(self, key_to_list_mapping): def __init__(self, key_to_list_mapping):