Commit fb56f215 by Tom Christie

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

parent 4231995f
...@@ -101,6 +101,12 @@ The arguments passed to `.parse()` are: ...@@ -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 ...@@ -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: ...@@ -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'
......
""" """
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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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:
......
""" """
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 ...@@ -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
......
...@@ -260,15 +260,19 @@ class Request(object): ...@@ -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:
......
...@@ -27,7 +27,7 @@ factory = RequestFactory() ...@@ -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)`.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment