diff options
| -rw-r--r-- | docs/api-guide/authentication.md | 4 | ||||
| -rw-r--r-- | docs/api-guide/exceptions.md | 2 | ||||
| -rw-r--r-- | docs/api-guide/pagination.md | 2 | ||||
| -rw-r--r-- | docs/api-guide/parsers.md | 26 | ||||
| -rw-r--r-- | docs/api-guide/permissions.md | 4 | ||||
| -rw-r--r-- | docs/api-guide/renderers.md | 34 | ||||
| -rw-r--r-- | docs/api-guide/throttling.md | 4 | ||||
| -rw-r--r-- | docs/topics/rest-hypermedia-hateoas.md | 6 | ||||
| -rw-r--r-- | rest_framework/authentication.py | 40 | ||||
| -rw-r--r-- | rest_framework/parsers.py | 21 | ||||
| -rw-r--r-- | rest_framework/permissions.py | 15 | ||||
| -rw-r--r-- | rest_framework/renderers.py | 4 | ||||
| -rw-r--r-- | rest_framework/request.py | 29 | ||||
| -rw-r--r-- | rest_framework/resources.py | 1 | ||||
| -rw-r--r-- | rest_framework/settings.py | 2 | ||||
| -rw-r--r-- | rest_framework/tests/request.py | 2 | ||||
| -rw-r--r-- | rest_framework/utils/mediatypes.py | 26 | ||||
| -rw-r--r-- | rest_framework/views.py | 22 | 
18 files changed, 119 insertions, 125 deletions
| diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index ae21c66e..71f48163 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -60,6 +60,8 @@ Or, if you're using the `@api_view` decorator with function based views.          }          return Response(content) +# API Reference +  ## BasicAuthentication  This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password.  Basic authentication is generally only appropriate for testing. @@ -113,7 +115,7 @@ If successfully authenticated, `SessionAuthentication` provides the following cr  * `request.user` will be a `django.contrib.auth.models.User` instance.  * `request.auth` will be `None`. -## Custom authentication policies +# Custom authentication  To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method.  The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index c22d6d8b..c3bdb7b9 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -37,7 +37,7 @@ Might recieve an error response indicating that the `DELETE` method is not allow  **Signature:** `APIException(detail=None)` -The base class for all exceptions raised inside REST framework. +The **base class** for all exceptions raised inside REST framework.  To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class. diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 50be59bf..e416de02 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -100,7 +100,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie  For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. -## Creating custom pagination serializers +## Custom pagination serializers  To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return. diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 4b769672..4f145ba3 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -65,7 +65,7 @@ Parses `YAML` request content.  Parses REST framework's default style of `XML` request content. -Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.  If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. @@ -95,7 +95,19 @@ To implement a custom parser, you should override `BaseParser`, set the `.media_  The method should return the data that will be used to populate the `request.DATA` property. -For example: +The arguments passed to `.parse_stream()` are: + +### stream + +A stream-like object representing the body of the request. + +### parser_context + +If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.  By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. + +## Example + +The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request.       class PlainTextParser(BaseParser):      """ @@ -110,16 +122,6 @@ For example:          """          return stream.read() -The arguments passed to `.parse_stream()` are: - -### stream - -A stream-like object representing the body of the request. - -### parser_context - -If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.  By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. -  ## Uploading file content  If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method.  `DataAndFiles` should be instantiated with two arguments.  The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property. diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index b6912d88..eb290849 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -54,6 +54,8 @@ Or, if you're using the `@api_view` decorator with function based views.          }          return Response(content) +# API Reference +  ## IsAuthenticated  The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise. @@ -86,7 +88,7 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `  The `DjangoModelPermissions` class also supports object-level permissions.  Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required. -## Custom permissions +# Custom permissions  To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method. diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 024a4ee2..c8addb32 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -98,7 +98,7 @@ Renders the request data into `YAML`.  Renders REST framework's default style of `XML` response content. -Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.  If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. @@ -154,21 +154,6 @@ Renders data into HTML for the Browseable API.  This renderer will determine whi  To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method. -For example: - -    from django.utils.encoding import smart_unicode -    from rest_framework import renderers - - -    class PlainText(renderers.BaseRenderer): -        media_type = 'text/plain' -        format = 'txt' -         -        def render(self, data, media_type=None, renderer_context=None): -            if isinstance(data, basestring): -                return data -            return smart_unicode(data) -  The arguments passed to the `.render()` method are:  ### `data` @@ -184,6 +169,23 @@ Optional. If provided, this is the accepted media type, as determined by the con  Optional. If provided, this is a dictionary of contextual information provided by the view.  By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`. +## Example + +The following is an example plaintext renderer that will return a response with the `data` parameter as the content of the response. + +    from django.utils.encoding import smart_unicode +    from rest_framework import renderers + + +    class PlainText(renderers.BaseRenderer): +        media_type = 'text/plain' +        format = 'txt' +         +        def render(self, data, media_type=None, renderer_context=None): +            if isinstance(data, basestring): +                return data +            return smart_unicode(data) +  ---  # Advanced renderer usage diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 0e228905..3fb95ae3 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -63,6 +63,8 @@ Or, if you're using the `@api_view` decorator with function based views.          }          return Response(content) +# API Reference +  ## AnonRateThrottle  The `AnonThrottle` will only ever throttle unauthenticated users.  The IP address of the incoming request is used to generate a unique key to throttle against. @@ -142,7 +144,7 @@ For example, given the following views...  User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day.  User requests to `UploadView` would be restricted to 20 requests per day. -## Custom throttles +# Custom throttles  To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`.  The method should return `True` if the request should be allowed, and `False` otherwise. diff --git a/docs/topics/rest-hypermedia-hateoas.md b/docs/topics/rest-hypermedia-hateoas.md index b58dfcb7..dda10eb4 100644 --- a/docs/topics/rest-hypermedia-hateoas.md +++ b/docs/topics/rest-hypermedia-hateoas.md @@ -24,15 +24,15 @@ For a more thorough background, check out Klabnik's [Hypermedia API reading list  REST framework is an agnositic Web API toolkit.  It does help guide you towards building well-connected APIs, and makes it easy to design appropriate media types, but it does not strictly enforce any particular design style. -### What REST framework *does* provide. +## What REST framework *does* provide.  It is self evident that REST framework makes it possible to build Hypermedia APIs.  The browseable API that it offers is built on HTML - the hypermedia language of the web.  REST framework also includes [serialization] and [parser]/[renderer] components that make it easy to build appropriate media types, [hyperlinked relations][fields] for building well-connected systems, and great support for [content negotiation][conneg]. -### What REST framework *doesn't* provide. +## What REST framework *doesn't* provide. -What REST framework doesn't do is give you is machine readable hypermedia formats such as [Collection+JSON][collection] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include form descriptions, and semantically labelled hyperlinks.  Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope. +What REST framework doesn't do is give you is machine readable hypermedia formats such as [Collection+JSON][collection] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks.  Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.  [cite]: http://vimeo.com/channels/restfest/page:2  [dissertation]: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index ee5bd2f2..d7624708 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -1,10 +1,9 @@  """ -The :mod:`authentication` module provides a set of pluggable authentication classes. - -Authentication behavior is provided by mixing the :class:`mixins.RequestMixin` class into a :class:`View` class. +Provides a set of pluggable authentication policies.  """  from django.contrib.auth import authenticate +from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError  from rest_framework.compat import CsrfViewMiddleware  from rest_framework.authtoken.models import Token  import base64 @@ -17,25 +16,14 @@ class BaseAuthentication(object):      def authenticate(self, request):          """ -        Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_ - -        .. [*] The authentication context *will* typically be a :obj:`User`, -            but it need not be.  It can be any user-like object so long as the -            permissions classes (see the :mod:`permissions` module) on the view can -            handle the object and use it to determine if the request has the required -            permissions or not. - -            This can be an important distinction if you're implementing some token -            based authentication mechanism, where the authentication context -            may be more involved than simply mapping to a :obj:`User`. +        Authenticate the request and return a two-tuple of (user, token).          """ -        return None +        raise NotImplementedError(".authenticate() must be overridden.")  class BasicAuthentication(BaseAuthentication):      """ -    Base class for HTTP Basic authentication. -    Subclasses should implement `.authenticate_credentials()`. +    HTTP Basic authentication against username/password.      """      def authenticate(self, request): @@ -43,8 +31,6 @@ class BasicAuthentication(BaseAuthentication):          Returns a `User` if a correct username and password have been supplied          using HTTP Basic authentication.  Otherwise returns `None`.          """ -        from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError -          if 'HTTP_AUTHORIZATION' in request.META:              auth = request.META['HTTP_AUTHORIZATION'].split()              if len(auth) == 2 and auth[0].lower() == "basic": @@ -54,7 +40,8 @@ class BasicAuthentication(BaseAuthentication):                      return None                  try: -                    userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2]) +                    userid = smart_unicode(auth_parts[0]) +                    password = smart_unicode(auth_parts[2])                  except DjangoUnicodeDecodeError:                      return None @@ -62,15 +49,6 @@ class BasicAuthentication(BaseAuthentication):      def authenticate_credentials(self, userid, password):          """ -        Given the Basic authentication userid and password, authenticate -        and return a user instance. -        """ -        raise NotImplementedError('.authenticate_credentials() must be overridden') - - -class UserBasicAuthentication(BasicAuthentication): -    def authenticate_credentials(self, userid, password): -        """          Authenticate the userid and password against username and password.          """          user = authenticate(username=userid, password=password) @@ -85,8 +63,8 @@ class SessionAuthentication(BaseAuthentication):      def authenticate(self, request):          """ -        Returns a :obj:`User` if the request session currently has a logged in user. -        Otherwise returns :const:`None`. +        Returns a `User` if the request session currently has a logged in user. +        Otherwise returns `None`.          """          # Get the underlying HttpRequest object diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 672f6a16..048b71e1 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -38,7 +38,7 @@ class BaseParser(object):      media_type = None -    def parse(self, string_or_stream, **opts): +    def parse(self, string_or_stream, parser_context=None):          """          The main entry point to parsers.  This is a light wrapper around          `parse_stream`, that instead handles both string and stream objects. @@ -47,9 +47,9 @@ class BaseParser(object):              stream = BytesIO(string_or_stream)          else:              stream = string_or_stream -        return self.parse_stream(stream, **opts) +        return self.parse_stream(stream, parser_context) -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Given a stream to read from, return the deserialized output.          Should return parsed data, or a DataAndFiles object consisting of the @@ -65,7 +65,7 @@ class JSONParser(BaseParser):      media_type = 'application/json' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Returns a 2-tuple of `(data, files)`. @@ -85,7 +85,7 @@ class YAMLParser(BaseParser):      media_type = 'application/yaml' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Returns a 2-tuple of `(data, files)`. @@ -105,7 +105,7 @@ class FormParser(BaseParser):      media_type = 'application/x-www-form-urlencoded' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Returns a 2-tuple of `(data, files)`. @@ -123,15 +123,16 @@ class MultiPartParser(BaseParser):      media_type = 'multipart/form-data' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Returns a DataAndFiles object.          `.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'] +        parser_context = parser_context or {} +        meta = parser_context['meta'] +        upload_handlers = parser_context['upload_handlers']          try:              parser = DjangoMultiPartParser(meta, stream, upload_handlers)              data, files = parser.parse() @@ -147,7 +148,7 @@ class XMLParser(BaseParser):      media_type = 'application/xml' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          try:              tree = ET.parse(stream)          except (ExpatError, ETParseError, ValueError), exc: diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 13ea39ea..6f848cee 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -1,8 +1,5 @@  """ -The :mod:`permissions` module bundles a set of permission classes that are used -for checking if a request passes a certain set of constraints. - -Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class. +Provides a set of pluggable permission policies.  """ @@ -16,7 +13,7 @@ class BasePermission(object):      def has_permission(self, request, view, obj=None):          """ -        Should simply return, or raise an :exc:`response.ImmediateResponse`. +        Return `True` if permission is granted, `False` otherwise.          """          raise NotImplementedError(".has_permission() must be overridden.") @@ -64,7 +61,8 @@ class DjangoModelPermissions(BasePermission):      It ensures that the user is authenticated, and has the appropriate      `add`/`change`/`delete` permissions on the model. -    This permission should only be used on views with a `ModelResource`. +    This permission will only be applied against view classes that +    provide a `.model` attribute, such as the generic class-based views.      """      # Map methods into required permission codes. @@ -92,7 +90,10 @@ class DjangoModelPermissions(BasePermission):          return [perm % kwargs for perm in self.perms_map[method]]      def has_permission(self, request, view, obj=None): -        model_cls = view.model +        model_cls = getattr(view, 'model', None) +        if not model_cls: +            return True +          perms = self.get_required_permissions(request.method, model_cls)          if (request.user and diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index e5e4134b..2a3b0b6c 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -7,6 +7,7 @@ and providing forms and links depending on the allowed methods, renderers and pa  """  import string  from django import forms +from django.http.multipartparser import parse_header  from django.template import RequestContext, loader  from django.utils import simplejson as json  from rest_framework.compat import yaml @@ -16,7 +17,6 @@ from rest_framework.request import clone_request  from rest_framework.utils import dict2xml  from rest_framework.utils import encoders  from rest_framework.utils.breadcrumbs import get_breadcrumbs -from rest_framework.utils.mediatypes import get_media_type_params  from rest_framework import VERSION  from rest_framework import serializers, parsers @@ -58,7 +58,7 @@ class JSONRenderer(BaseRenderer):          if accepted_media_type:              # If the media type looks like 'application/json; indent=4',              # then pretty print the result. -            params = get_media_type_params(accepted_media_type) +            base_media_type, params = parse_header(accepted_media_type)              indent = params.get('indent', indent)              try:                  indent = max(min(int(indent), 8), 0) diff --git a/rest_framework/request.py b/rest_framework/request.py index 7267b368..6f9cf09a 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -11,9 +11,18 @@ The wrapped request then offers a richer API, in particular :  """  from StringIO import StringIO +from django.http.multipartparser import parse_header  from rest_framework import exceptions  from rest_framework.settings import api_settings -from rest_framework.utils.mediatypes import is_form_media_type + + +def is_form_media_type(media_type): +    """ +    Return True if the media type is a valid form media type. +    """ +    base_media_type, params = parse_header(media_type) +    return base_media_type == 'application/x-www-form-urlencoded' or \ +           base_media_type == 'multipart/form-data'  class Empty(object): @@ -35,7 +44,8 @@ def clone_request(request, method):      """      ret = Request(request._request,                    request.parsers, -                  request.authenticators) +                  request.authenticators, +                  request.parser_context)      ret._data = request._data      ret._files = request._files      ret._content_type = request._content_type @@ -65,20 +75,30 @@ class Request(object):      _CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE      def __init__(self, request, parsers=None, authenticators=None, -                 negotiator=None): +                 negotiator=None, parser_context=None):          self._request = request          self.parsers = parsers or ()          self.authenticators = authenticators or ()          self.negotiator = negotiator or self._default_negotiator() +        self.parser_context = parser_context          self._data = Empty          self._files = Empty          self._method = Empty          self._content_type = Empty          self._stream = Empty +        if self.parser_context is None: +            self.parser_context = self._default_parser_context(request) +      def _default_negotiator(self):          return api_settings.DEFAULT_CONTENT_NEGOTIATION() +    def _default_parser_context(self, request): +        return { +            'upload_handlers': request.upload_handlers, +            'meta': request.META, +        } +      @property      def method(self):          """ @@ -253,8 +273,7 @@ class Request(object):          if not parser:              raise exceptions.UnsupportedMediaType(self.content_type) -        parsed = parser.parse(self.stream, meta=self.META, -                              upload_handlers=self.upload_handlers) +        parsed = parser.parse(self.stream, self.parser_context)          # Parser classes may return the raw data, or a          # DataAndFiles object.  Unpack the result as required.          try: diff --git a/rest_framework/resources.py b/rest_framework/resources.py index bb3d581f..dd8a5471 100644 --- a/rest_framework/resources.py +++ b/rest_framework/resources.py @@ -70,6 +70,7 @@ class Resource(ResourceMixin, views.APIView):  ##### RESOURCES AND ROUTERS ARE NOT YET IMPLEMENTED - PLACEHOLDER ONLY #####  class ModelResource(ResourceMixin, views.APIView): +    # TODO: Actually delegation won't work      root_class = generics.ListCreateAPIView      detail_class = generics.RetrieveUpdateDestroyAPIView diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 5ebe7ba5..8bbb2f75 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -35,7 +35,7 @@ DEFAULTS = {      ),      'DEFAULT_AUTHENTICATION': (          'rest_framework.authentication.SessionAuthentication', -        'rest_framework.authentication.UserBasicAuthentication' +        'rest_framework.authentication.BasicAuthentication'      ),      'DEFAULT_PERMISSIONS': (),      'DEFAULT_THROTTLES': (), diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index f00ee85f..f90bebf4 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -27,7 +27,7 @@ factory = RequestFactory()  class PlainTextParser(BaseParser):      media_type = 'text/plain' -    def parse_stream(self, stream, **opts): +    def parse_stream(self, stream, parser_context=None):          """          Returns a 2-tuple of `(data, files)`. diff --git a/rest_framework/utils/mediatypes.py b/rest_framework/utils/mediatypes.py index 5eba7fb2..ee7f3a54 100644 --- a/rest_framework/utils/mediatypes.py +++ b/rest_framework/utils/mediatypes.py @@ -25,32 +25,6 @@ def media_type_matches(lhs, rhs):      return lhs.match(rhs) -def is_form_media_type(media_type): -    """ -    Return True if the media type is a valid form media type as defined by the HTML4 spec. -    (NB. HTML5 also adds text/plain to the list of valid form media types, but we don't support this here) -    """ -    media_type = _MediaType(media_type) -    return media_type.full_type == 'application/x-www-form-urlencoded' or \ -           media_type.full_type == 'multipart/form-data' - - -def add_media_type_param(media_type, key, val): -    """ -    Add a key, value parameter to a media type string, and return the new media type string. -    """ -    media_type = _MediaType(media_type) -    media_type.params[key] = val -    return str(media_type) - - -def get_media_type_params(media_type): -    """ -    Return a dictionary of the parameters on the given media type. -    """ -    return _MediaType(media_type).params - -  def order_by_precedence(media_type_lst):      """      Returns a list of sets of media type strings, ordered by precedence. diff --git a/rest_framework/views.py b/rest_framework/views.py index b3f36085..92d4445f 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -1,8 +1,5 @@  """ -The :mod:`views` module provides the Views you will most probably -be subclassing in your implementation. - -By setting or modifying class attributes on your view, you change it's predefined behaviour. +Provides an APIView class that is used as the base of all class-based views.  """  import re @@ -159,9 +156,19 @@ class APIView(View):          """          raise exceptions.Throttled(wait) +    def get_parser_context(self, request): +        """ +        Returns a dict that is passed through to Parser.parse_stream(), +        as the `parser_context` keyword argument. +        """ +        return { +            'upload_handlers': request.upload_handlers, +            'meta': request.META, +        } +      def get_renderer_context(self):          """ -        Returns a dict that is passed through to the Renderer.render(), +        Returns a dict that is passed through to Renderer.render(),          as the `renderer_context` keyword argument.          """          # Note: Additionally 'response' will also be set on the context, @@ -253,10 +260,13 @@ class APIView(View):          """          Returns the initial request object.          """ +        parser_context = self.get_parser_context(request) +          return Request(request,                         parsers=self.get_parsers(),                         authenticators=self.get_authenticators(), -                       negotiator=self.get_content_negotiator()) +                       negotiator=self.get_content_negotiator(), +                       parser_context=parser_context)      def initial(self, request, *args, **kwargs):          """ | 
