Added media_type to .parse() - Consistency with renderer API.

This commit is contained in:
Tom Christie 2012-10-17 22:39:07 +01:00
parent 4231995fbd
commit fb56f215ae
5 changed files with 35 additions and 30 deletions

View File

@ -101,6 +101,12 @@ The arguments passed to `.parse()` are:
A stream-like object representing the body of the request. A stream-like object representing the body of the request.
### media_type
Optional. If provided, this is the media type of the incoming request.
Depending on the request's `Content-Type:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"text/plain; charset=utf-8"`.
### parser_context ### parser_context
Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.
@ -118,7 +124,7 @@ The following is an example plaintext parser that will populate the `request.DAT
media_type = 'text/plain' media_type = 'text/plain'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Simply return a string representing the body of the request. Simply return a string representing the body of the request.
""" """
@ -135,7 +141,7 @@ For example:
A naive raw file upload parser. A naive raw file upload parser.
""" """
def parse(self, stream, parser_context): def parse(self, stream, media_type=None, parser_context=None):
content = stream.read() content = stream.read()
name = 'example.dat' name = 'example.dat'
content_type = 'application/octet-stream' content_type = 'application/octet-stream'

View File

@ -1,14 +1,8 @@
""" """
Django supports parsing the content of an HTTP request, but only for form POST requests. Parsers are used to parse the content of incoming HTTP requests.
That behavior is sufficient for dealing with standard HTML forms, but it doesn't map well
to general HTTP requests.
We need a method to be able to: They give us a generic way of being able to handle various media types
on the request, such as form content or json encoded data.
1.) Determine the parsed content on a request for methods other than POST (eg typically also PUT)
2.) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded
and multipart/form-data. (eg also handle multipart/json)
""" """
from django.http import QueryDict from django.http import QueryDict
@ -37,10 +31,10 @@ class BaseParser(object):
media_type = None media_type = None
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Given a stream to read from, return the deserialized output. Given a stream to read from, return the parsed representation.
Should return parsed data, or a DataAndFiles object consisting of the Should return parsed data, or a `DataAndFiles` object consisting of the
parsed data and files. parsed data and files.
""" """
raise NotImplementedError(".parse() must be overridden.") raise NotImplementedError(".parse() must be overridden.")
@ -53,7 +47,7 @@ class JSONParser(BaseParser):
media_type = 'application/json' media_type = 'application/json'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Returns a 2-tuple of `(data, files)`. Returns a 2-tuple of `(data, files)`.
@ -73,7 +67,7 @@ class YAMLParser(BaseParser):
media_type = 'application/yaml' media_type = 'application/yaml'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Returns a 2-tuple of `(data, files)`. Returns a 2-tuple of `(data, files)`.
@ -93,7 +87,7 @@ class FormParser(BaseParser):
media_type = 'application/x-www-form-urlencoded' media_type = 'application/x-www-form-urlencoded'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Returns a 2-tuple of `(data, files)`. Returns a 2-tuple of `(data, files)`.
@ -111,7 +105,7 @@ class MultiPartParser(BaseParser):
media_type = 'multipart/form-data' media_type = 'multipart/form-data'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Returns a DataAndFiles object. Returns a DataAndFiles object.
@ -138,7 +132,7 @@ class XMLParser(BaseParser):
media_type = 'application/xml' media_type = 'application/xml'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
try: try:
tree = ET.parse(stream) tree = ET.parse(stream)
except (ExpatError, ETParseError, ValueError), exc: except (ExpatError, ETParseError, ValueError), exc:

View File

@ -1,9 +1,10 @@
""" """
Renderers are used to serialize a View's output into specific media types. Renderers are used to serialize a response into specific media types.
Django REST framework also provides HTML and PlainText renderers that help self-document the API, They give us a generic way of being able to handle various media types
by serializing the output along with documentation regarding the View, output status and headers, on the response, such as JSON encoded data or HTML output.
and providing forms and links depending on the allowed methods, renderers and parsers on the View.
REST framework also provides an HTML renderer the renders the browseable API.
""" """
import string import string
from django import forms from django import forms
@ -23,8 +24,8 @@ from rest_framework import serializers, parsers
class BaseRenderer(object): class BaseRenderer(object):
""" """
All renderers must extend this class, set the :attr:`media_type` attribute, All renderers should extend this class, setting the `media_type`
and override the :meth:`render` method. and `format` attributes, and override the `.render()` method.
""" """
media_type = None media_type = None

View File

@ -260,15 +260,19 @@ class Request(object):
May raise an `UnsupportedMediaType`, or `ParseError` exception. May raise an `UnsupportedMediaType`, or `ParseError` exception.
""" """
if self.stream is None or self.content_type is None: stream = self.stream
media_type = self.content_type
if stream is None or media_type is None:
return (None, None) return (None, None)
parser = self.negotiator.select_parser(self.parsers, self.content_type) parser = self.negotiator.select_parser(self.parsers, media_type)
if not parser: if not parser:
raise exceptions.UnsupportedMediaType(self.content_type) raise exceptions.UnsupportedMediaType(media_type)
parsed = parser.parse(stream, media_type, self.parser_context)
parsed = parser.parse(self.stream, self.parser_context)
# Parser classes may return the raw data, or a # Parser classes may return the raw data, or a
# DataAndFiles object. Unpack the result as required. # DataAndFiles object. Unpack the result as required.
try: try:

View File

@ -27,7 +27,7 @@ factory = RequestFactory()
class PlainTextParser(BaseParser): class PlainTextParser(BaseParser):
media_type = 'text/plain' media_type = 'text/plain'
def parse(self, stream, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
Returns a 2-tuple of `(data, files)`. Returns a 2-tuple of `(data, files)`.