diff options
| author | Tom Christie | 2012-09-03 14:28:40 +0100 | 
|---|---|---|
| committer | Tom Christie | 2012-09-03 14:28:40 +0100 | 
| commit | 7abef9ac3b3fb20a6cdef5d52c640e5725c93437 (patch) | |
| tree | 2cd3270b7079f8b20eb3e3e90d8181c37c4199cb | |
| parent | d180e984e9779673c2d8aab3c65b4763432ba6b5 (diff) | |
| download | django-rest-framework-7abef9ac3b3fb20a6cdef5d52c640e5725c93437.tar.bz2 | |
Parsers may return raw data, or a DataAndFiles object
| -rw-r--r-- | djangorestframework/parsers.py | 31 | ||||
| -rw-r--r-- | djangorestframework/request.py | 16 | ||||
| -rw-r--r-- | djangorestframework/tests/parsers.py | 6 | ||||
| -rw-r--r-- | djangorestframework/tests/renderers.py | 4 | ||||
| -rw-r--r-- | djangorestframework/tests/request.py | 2 | ||||
| -rw-r--r-- | djangorestframework/utils/encoders.py | 9 | ||||
| -rw-r--r-- | docs/tutorial/1-serialization.md | 2 | 
7 files changed, 40 insertions, 30 deletions
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py index 43ea0c4d..fc0260fc 100644 --- a/djangorestframework/parsers.py +++ b/djangorestframework/parsers.py @@ -36,6 +36,12 @@ __all__ = (  ) +class DataAndFiles(object): +    def __init__(self, data, files): +        self.data = data +        self.files = files + +  class BaseParser(object):      """      All parsers should extend :class:`BaseParser`, specifying a :attr:`media_type` attribute, @@ -80,7 +86,7 @@ class JSONParser(BaseParser):          `files` will always be `None`.          """          try: -            return (json.load(stream), None) +            return json.load(stream)          except ValueError, exc:              raise ParseError('JSON parse error - %s' % unicode(exc)) @@ -100,7 +106,7 @@ class YAMLParser(BaseParser):          `files` will always be `None`.          """          try: -            return (yaml.safe_load(stream), None) +            return yaml.safe_load(stream)          except (ValueError, yaml.parser.ParserError), exc:              raise ParseError('YAML parse error - %s' % unicode(exc)) @@ -119,7 +125,7 @@ class PlainTextParser(BaseParser):          `data` will simply be a string representing the body of the request.          `files` will always be `None`.          """ -        return (stream.read(), None) +        return stream.read()  class FormParser(BaseParser): @@ -137,7 +143,7 @@ class FormParser(BaseParser):          `files` will always be :const:`None`.          """          data = QueryDict(stream.read()) -        return (data, None) +        return data  class MultiPartParser(BaseParser): @@ -149,16 +155,17 @@ class MultiPartParser(BaseParser):      def parse(self, stream, **opts):          """ -        Returns a 2-tuple of `(data, files)`. +        Returns a DataAndFiles object. -        `data` will be a :class:`QueryDict` containing all the form parameters. -        `files` will be a :class:`QueryDict` containing all the form files. +        `.data` will be a `QueryDict` containing all the form parameters. +        `.files` will be a `QueryDict` containing all the form files.          """          meta = opts['meta']          upload_handlers = opts['upload_handlers']          try:              parser = DjangoMultiPartParser(meta, stream, upload_handlers) -            return parser.parse() +            data, files = parser.parse() +            return DataAndFiles(data, files)          except MultiPartParserError, exc:              raise ParseError('Multipart form parse error - %s' % unicode(exc)) @@ -171,19 +178,13 @@ class XMLParser(BaseParser):      media_type = 'application/xml'      def parse(self, stream, **opts): -        """ -        Returns a 2-tuple of `(data, files)`. - -        `data` will simply be a string representing the body of the request. -        `files` will always be `None`. -        """          try:              tree = ET.parse(stream)          except (ExpatError, ETParseError, ValueError), exc:              raise ParseError('XML parse error - %s' % unicode(exc))          data = self._xml_convert(tree.getroot()) -        return (data, None) +        return data      def _xml_convert(self, element):          """ diff --git a/djangorestframework/request.py b/djangorestframework/request.py index 84ca0575..2e4e8909 100644 --- a/djangorestframework/request.py +++ b/djangorestframework/request.py @@ -146,7 +146,7 @@ class Request(object):              self._load_method_and_content_type()          if not _hasattr(self, '_data'): -            (self._data, self._files) = self._parse() +            self._data, self._files = self._parse()      def _load_method_and_content_type(self):          """ @@ -201,11 +201,11 @@ class Request(object):              self._CONTENTTYPE_PARAM in self._data):              self._content_type = self._data.pop(self._CONTENTTYPE_PARAM)[0]              self._stream = StringIO(self._data.pop(self._CONTENT_PARAM)[0]) -            (self._data, self._files) = self._parse() +            self._data, self._files = self._parse()      def _parse(self):          """ -        Parse the request content. +        Parse the request content, returning a two-tuple of (data, files)          May raise an `UnsupportedMediaType`, or `ParseError` exception.          """ @@ -214,8 +214,14 @@ class Request(object):          for parser in self.get_parsers():              if parser.can_handle_request(self.content_type): -                return parser.parse(self.stream, meta=self.META, -                                    upload_handlers=self.upload_handlers) +                parsed = parser.parse(self.stream, meta=self.META, +                                      upload_handlers=self.upload_handlers) +                # Parser classes may return the raw data, or a +                # DataAndFiles object.  Unpack the result as required. +                try: +                    return (parsed.data, parsed.files) +                except AttributeError: +                    return (parsed, None)          raise UnsupportedMediaType(self._content_type) diff --git a/djangorestframework/tests/parsers.py b/djangorestframework/tests/parsers.py index a85409dc..c9b6afd0 100644 --- a/djangorestframework/tests/parsers.py +++ b/djangorestframework/tests/parsers.py @@ -153,7 +153,7 @@ class TestFormParser(TestCase):          parser = FormParser()          stream = StringIO(self.string) -        (data, files) = parser.parse(stream) +        data = parser.parse(stream)          self.assertEqual(Form(data).is_valid(), True) @@ -203,10 +203,10 @@ class TestXMLParser(TestCase):      def test_parse(self):          parser = XMLParser() -        (data, files) = parser.parse(self._input) +        data = parser.parse(self._input)          self.assertEqual(data, self._data)      def test_complex_data_parse(self):          parser = XMLParser() -        (data, files) = parser.parse(self._complex_data_input) +        data = parser.parse(self._complex_data_input)          self.assertEqual(data, self._complex_data) diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py index adf8d8fa..dc30f487 100644 --- a/djangorestframework/tests/renderers.py +++ b/djangorestframework/tests/renderers.py @@ -301,7 +301,7 @@ if YAMLRenderer:              parser = YAMLParser()              content = renderer.render(obj, 'application/yaml') -            (data, files) = parser.parse(StringIO(content)) +            data = parser.parse(StringIO(content))              self.assertEquals(obj, data) @@ -392,7 +392,7 @@ class XMLRendererTestCase(TestCase):          content = StringIO(renderer.render(self._complex_data, 'application/xml'))          parser = XMLParser() -        complex_data_out, dummy = parser.parse(content) +        complex_data_out = parser.parse(content)          error_msg = "complex data differs!IN:\n %s \n\n OUT:\n %s" % (repr(self._complex_data), repr(complex_data_out))          self.assertEqual(self._complex_data, complex_data_out, error_msg) diff --git a/djangorestframework/tests/request.py b/djangorestframework/tests/request.py index 85b2f418..1d74ea32 100644 --- a/djangorestframework/tests/request.py +++ b/djangorestframework/tests/request.py @@ -4,7 +4,6 @@ Tests for content parsing, and form-overloaded content parsing.  from django.conf.urls.defaults import patterns  from django.contrib.auth.models import User  from django.test import TestCase, Client -from django.utils import simplejson as json  from djangorestframework import status  from djangorestframework.authentication import UserLoggedInAuthentication @@ -13,7 +12,6 @@ from djangorestframework.parsers import (      FormParser,      MultiPartParser,      PlainTextParser, -    JSONParser  )  from djangorestframework.request import Request  from djangorestframework.response import Response diff --git a/djangorestframework/utils/encoders.py b/djangorestframework/utils/encoders.py index 3cd2e8e1..ba7c8553 100644 --- a/djangorestframework/utils/encoders.py +++ b/djangorestframework/utils/encoders.py @@ -1,3 +1,6 @@ +""" +Helper classes for parsers. +"""  import datetime  import decimal  from django.utils import timezone @@ -6,10 +9,12 @@ from django.utils import simplejson as json  class JSONEncoder(json.JSONEncoder):      """ -    JSONEncoder subclass that knows how to encode date/time and decimal types. +    JSONEncoder subclass that knows how to encode date/time, +    decimal types, and generators.      """      def default(self, o): -        # See "Date Time String Format" in the ECMA-262 specification. +        # For Date Time string spec, see ECMA 262 +        # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15          if isinstance(o, datetime.datetime):              r = o.isoformat()              if o.microsecond: diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 0b6eac9d..34bac155 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -127,7 +127,7 @@ We've now got a few comment instances to play with.  Let's take a look at serial      serializer = CommentSerializer(instance=c1)      serializer.data -    # {'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)} +    # {'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774, tzinfo=<UTC>)}  At this point we've translated the model instance into python native datatypes.  To finalise the serialization process we render the data into `json`.  | 
