mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 01:47:59 +03:00 
			
		
		
		
	Moved content negotiation out of response. Nicer exception handling now.
This commit is contained in:
		
							parent
							
								
									b7b8cd11b1
								
							
						
					
					
						commit
						b3e29d9576
					
				
							
								
								
									
										63
									
								
								djangorestframework/contentnegotiation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								djangorestframework/contentnegotiation.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					from djangorestframework import exceptions
 | 
				
			||||||
 | 
					from djangorestframework.settings import api_settings
 | 
				
			||||||
 | 
					from djangorestframework.utils.mediatypes import order_by_precedence
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseContentNegotiation(object):
 | 
				
			||||||
 | 
					    def determine_renderer(self, request, renderers):
 | 
				
			||||||
 | 
					        raise NotImplementedError('.determine_renderer() must be implemented')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DefaultContentNegotiation(object):
 | 
				
			||||||
 | 
					    settings = api_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def negotiate(self, request, renderers):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Given a request and a list of renderers, return a two-tuple of:
 | 
				
			||||||
 | 
					        (renderer, media type).
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        accepts = self.get_accept_list(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Check the acceptable media types against each renderer,
 | 
				
			||||||
 | 
					        # attempting more specific media types first
 | 
				
			||||||
 | 
					        # NB. The inner loop here isn't as bad as it first looks :)
 | 
				
			||||||
 | 
					        #     Worst case is we're looping over len(accept_list) * len(self.renderers)
 | 
				
			||||||
 | 
					        for media_type_set in order_by_precedence(accepts):
 | 
				
			||||||
 | 
					            for renderer in renderers:
 | 
				
			||||||
 | 
					                for media_type in media_type_set:
 | 
				
			||||||
 | 
					                    if renderer.can_handle_media_type(media_type):
 | 
				
			||||||
 | 
					                        return renderer, media_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        raise exceptions.NotAcceptable(available_renderers=renderers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_accept_list(self, request):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Given the incoming request, return a tokenised list of
 | 
				
			||||||
 | 
					        media type strings.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.settings.URL_ACCEPT_OVERRIDE:
 | 
				
			||||||
 | 
					            # URL style accept override.  eg.  "?accept=application/json"
 | 
				
			||||||
 | 
					            override = request.GET.get(self.settings.URL_ACCEPT_OVERRIDE)
 | 
				
			||||||
 | 
					            if override:
 | 
				
			||||||
 | 
					                return [override]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (self.settings.IGNORE_MSIE_ACCEPT_HEADER and
 | 
				
			||||||
 | 
					            'HTTP_USER_AGENT' in request.META and
 | 
				
			||||||
 | 
					            MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
 | 
				
			||||||
 | 
					            request.META.get('HTTP_X_REQUESTED_WITH', '').lower() != 'xmlhttprequest'):
 | 
				
			||||||
 | 
					            # Ignore MSIE's broken accept behavior except for AJAX requests
 | 
				
			||||||
 | 
					            # and do something sensible instead
 | 
				
			||||||
 | 
					            return ['text/html', '*/*']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'HTTP_ACCEPT' in request.META:
 | 
				
			||||||
 | 
					            # Standard HTTP Accept negotiation
 | 
				
			||||||
 | 
					            # Accept header specified
 | 
				
			||||||
 | 
					            tokens = request.META['HTTP_ACCEPT'].split(',')
 | 
				
			||||||
 | 
					            return [token.strip() for token in tokens]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Standard HTTP Accept negotiation
 | 
				
			||||||
 | 
					        # No accept header specified
 | 
				
			||||||
 | 
					        return ['*/*']
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,15 @@ class MethodNotAllowed(APIException):
 | 
				
			||||||
        self.detail = (detail or self.default_detail) % method
 | 
					        self.detail = (detail or self.default_detail) % method
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NotAcceptable(APIException):
 | 
				
			||||||
 | 
					    status_code = status.HTTP_406_NOT_ACCEPTABLE
 | 
				
			||||||
 | 
					    default_detail = "Could not satisfy the request's Accept header"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, detail=None, available_renderers=None):
 | 
				
			||||||
 | 
					        self.detail = detail or self.default_detail
 | 
				
			||||||
 | 
					        self.available_renderers = available_renderers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UnsupportedMediaType(APIException):
 | 
					class UnsupportedMediaType(APIException):
 | 
				
			||||||
    status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
 | 
					    status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
 | 
				
			||||||
    default_detail = "Unsupported media type '%s' in request."
 | 
					    default_detail = "Unsupported media type '%s' in request."
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,28 +48,22 @@ class BaseRenderer(object):
 | 
				
			||||||
    def __init__(self, view=None):
 | 
					    def __init__(self, view=None):
 | 
				
			||||||
        self.view = view
 | 
					        self.view = view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_handle_response(self, accept):
 | 
					    def can_handle_format(self, format):
 | 
				
			||||||
        """
 | 
					        return format == self.format
 | 
				
			||||||
        Returns :const:`True` if this renderer is able to deal with the given
 | 
					 | 
				
			||||||
        *accept* media type.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        The default implementation for this function is to check the *accept*
 | 
					    def can_handle_media_type(self, media_type):
 | 
				
			||||||
        argument against the :attr:`media_type` attribute set on the class to see if
 | 
					        """
 | 
				
			||||||
 | 
					        Returns `True` if this renderer is able to deal with the given
 | 
				
			||||||
 | 
					        media type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The default implementation for this function is to check the media type
 | 
				
			||||||
 | 
					        argument against the media_type attribute set on the class to see if
 | 
				
			||||||
        they match.
 | 
					        they match.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This may be overridden to provide for other behavior, but typically you'll
 | 
					        This may be overridden to provide for other behavior, but typically
 | 
				
			||||||
        instead want to just set the :attr:`media_type` attribute on the class.
 | 
					        you'll instead want to just set the `media_type` attribute on the class.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        # TODO: format overriding must go out of here
 | 
					        return media_type_matches(self.media_type, media_type)
 | 
				
			||||||
        format = None
 | 
					 | 
				
			||||||
        if self.view is not None:
 | 
					 | 
				
			||||||
            format = self.view.kwargs.get(self._FORMAT_QUERY_PARAM, None)
 | 
					 | 
				
			||||||
        if format is None and self.view is not None:
 | 
					 | 
				
			||||||
            format = self.view.request.GET.get(self._FORMAT_QUERY_PARAM, None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if format is not None:
 | 
					 | 
				
			||||||
            return format == self.format
 | 
					 | 
				
			||||||
        return media_type_matches(self.media_type, accept)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, obj=None, media_type=None):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,97 +1,34 @@
 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
`Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned
 | 
					 | 
				
			||||||
from any view. It is a bit smarter than Django's `HttpResponse`, for it renders automatically
 | 
					 | 
				
			||||||
its content to a serial format by using a list of :mod:`renderers`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To determine the content type to which it must render, default behaviour is to use standard
 | 
					 | 
				
			||||||
HTTP Accept header content negotiation. But `Response` also supports overriding the content type
 | 
					 | 
				
			||||||
by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers
 | 
					 | 
				
			||||||
from Internet Explorer user agents and use a sensible browser `Accept` header instead.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
from django.template.response import SimpleTemplateResponse
 | 
					from django.template.response import SimpleTemplateResponse
 | 
				
			||||||
from django.core.handlers.wsgi import STATUS_CODE_TEXT
 | 
					from django.core.handlers.wsgi import STATUS_CODE_TEXT
 | 
				
			||||||
from djangorestframework.settings import api_settings
 | 
					 | 
				
			||||||
from djangorestframework.utils.mediatypes import order_by_precedence
 | 
					 | 
				
			||||||
from djangorestframework import status
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NotAcceptable(Exception):
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Response(SimpleTemplateResponse):
 | 
					class Response(SimpleTemplateResponse):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    An HttpResponse that may include content that hasn't yet been serialized.
 | 
					    An HttpResponse that allows it's data to be rendered into
 | 
				
			||||||
 | 
					    arbitrary media types.
 | 
				
			||||||
    Kwargs:
 | 
					 | 
				
			||||||
        - content(object). The raw content, not yet serialized.
 | 
					 | 
				
			||||||
        This must be native Python data that renderers can handle.
 | 
					 | 
				
			||||||
        (e.g.: `dict`, `str`, ...)
 | 
					 | 
				
			||||||
        - renderer_classes(list/tuple). The renderers to use for rendering the response content.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE
 | 
					    def __init__(self, data=None, status=None, headers=None,
 | 
				
			||||||
    _IGNORE_IE_ACCEPT_HEADER = True
 | 
					                 renderer=None, media_type=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Alters the init arguments slightly.
 | 
				
			||||||
 | 
					        For example, drop 'template_name', and instead use 'data'.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, content=None, status=None, headers=None, view=None,
 | 
					        Setting 'renderer' and 'media_type' will typically be defered,
 | 
				
			||||||
                 request=None, renderer_classes=None, format=None):
 | 
					        For example being set automatically by the `APIView`.
 | 
				
			||||||
        # First argument taken by `SimpleTemplateResponse.__init__` is template_name,
 | 
					        """
 | 
				
			||||||
        # which we don't need
 | 
					 | 
				
			||||||
        super(Response, self).__init__(None, status=status)
 | 
					        super(Response, self).__init__(None, status=status)
 | 
				
			||||||
 | 
					        self.data = data
 | 
				
			||||||
        self.raw_content = content
 | 
					 | 
				
			||||||
        self.has_content_body = content is not None
 | 
					 | 
				
			||||||
        self.headers = headers and headers[:] or []
 | 
					        self.headers = headers and headers[:] or []
 | 
				
			||||||
        self.view = view
 | 
					        self.renderer = renderer
 | 
				
			||||||
        self.request = request
 | 
					        self.media_type = media_type
 | 
				
			||||||
        self.renderer_classes = renderer_classes
 | 
					 | 
				
			||||||
        self.format = format
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_renderers(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Instantiates and returns the list of renderers the response will use.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.renderer_classes is None:
 | 
					 | 
				
			||||||
            renderer_classes = api_settings.DEFAULT_RENDERERS
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            renderer_classes = self.renderer_classes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.format:
 | 
					 | 
				
			||||||
            return [cls(self.view) for cls in renderer_classes
 | 
					 | 
				
			||||||
                    if cls.format == self.format]
 | 
					 | 
				
			||||||
        return [cls(self.view) for cls in renderer_classes]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def rendered_content(self):
 | 
					    def rendered_content(self):
 | 
				
			||||||
        """
 | 
					        self['Content-Type'] = self.media_type
 | 
				
			||||||
        The final rendered content. Accessing this attribute triggers the
 | 
					        if self.data is None:
 | 
				
			||||||
        complete rendering cycle: selecting suitable renderer, setting
 | 
					            return self.renderer.render()
 | 
				
			||||||
        response's actual content type, rendering data.
 | 
					        return self.renderer.render(self.data, self.media_type)
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        renderer, media_type = self._determine_renderer()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Set the media type of the response
 | 
					 | 
				
			||||||
        self['Content-Type'] = renderer.media_type
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Render the response content
 | 
					 | 
				
			||||||
        if self.has_content_body:
 | 
					 | 
				
			||||||
            return renderer.render(self.raw_content, media_type)
 | 
					 | 
				
			||||||
        return renderer.render()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def render(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            return super(Response, self).render()
 | 
					 | 
				
			||||||
        except NotAcceptable:
 | 
					 | 
				
			||||||
            response = self._get_406_response()
 | 
					 | 
				
			||||||
            return response.render()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def status_text(self):
 | 
					    def status_text(self):
 | 
				
			||||||
| 
						 | 
					@ -100,74 +37,3 @@ class Response(SimpleTemplateResponse):
 | 
				
			||||||
        Provided for convenience.
 | 
					        Provided for convenience.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return STATUS_CODE_TEXT.get(self.status_code, '')
 | 
					        return STATUS_CODE_TEXT.get(self.status_code, '')
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _determine_accept_list(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Returns a list of accepted media types. This list is determined from :
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            1. overload with `_ACCEPT_QUERY_PARAM`
 | 
					 | 
				
			||||||
            2. `Accept` header of the request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        If those are useless, a default value is returned instead.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        request = self.request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (self._ACCEPT_QUERY_PARAM and
 | 
					 | 
				
			||||||
            request.GET.get(self._ACCEPT_QUERY_PARAM, None)):
 | 
					 | 
				
			||||||
            # Use _accept parameter override
 | 
					 | 
				
			||||||
            return [request.GET.get(self._ACCEPT_QUERY_PARAM)]
 | 
					 | 
				
			||||||
        elif (self._IGNORE_IE_ACCEPT_HEADER and
 | 
					 | 
				
			||||||
              'HTTP_USER_AGENT' in request.META and
 | 
					 | 
				
			||||||
              MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
 | 
					 | 
				
			||||||
              request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'):
 | 
					 | 
				
			||||||
            # Ignore MSIE's broken accept behavior except for AJAX requests
 | 
					 | 
				
			||||||
            # and do something sensible instead
 | 
					 | 
				
			||||||
            return ['text/html', '*/*']
 | 
					 | 
				
			||||||
        elif 'HTTP_ACCEPT' in request.META:
 | 
					 | 
				
			||||||
            # Use standard HTTP Accept negotiation
 | 
					 | 
				
			||||||
            return [token.strip() for token in request.META['HTTP_ACCEPT'].split(',')]
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # No accept header specified
 | 
					 | 
				
			||||||
            return ['*/*']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _determine_renderer(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Determines the appropriate renderer for the output, given the list of
 | 
					 | 
				
			||||||
        accepted media types, and the :attr:`renderer_classes` set on this class.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Returns a 2-tuple of `(renderer, media_type)`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        See: RFC 2616, Section 14
 | 
					 | 
				
			||||||
        http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        renderers = self.get_renderers()
 | 
					 | 
				
			||||||
        accepts = self._determine_accept_list()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Not acceptable response - Ignore accept header.
 | 
					 | 
				
			||||||
        if self.status_code == 406:
 | 
					 | 
				
			||||||
            return (renderers[0], renderers[0].media_type)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Check the acceptable media types against each renderer,
 | 
					 | 
				
			||||||
        # attempting more specific media types first
 | 
					 | 
				
			||||||
        # NB. The inner loop here isn't as bad as it first looks :)
 | 
					 | 
				
			||||||
        #     Worst case is we're looping over len(accept_list) * len(self.renderers)
 | 
					 | 
				
			||||||
        for media_type_set in order_by_precedence(accepts):
 | 
					 | 
				
			||||||
            for renderer in renderers:
 | 
					 | 
				
			||||||
                for media_type in media_type_set:
 | 
					 | 
				
			||||||
                    if renderer.can_handle_response(media_type):
 | 
					 | 
				
			||||||
                        return renderer, media_type
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # No acceptable renderers were found
 | 
					 | 
				
			||||||
        raise NotAcceptable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _get_406_response(self):
 | 
					 | 
				
			||||||
        renderer = self.renderer_classes[0]
 | 
					 | 
				
			||||||
        return Response(
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                'detail': 'Could not satisfy the client\'s Accept header',
 | 
					 | 
				
			||||||
                'available_types': [renderer.media_type
 | 
					 | 
				
			||||||
                                    for renderer in self.renderer_classes]
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            status=status.HTTP_406_NOT_ACCEPTABLE,
 | 
					 | 
				
			||||||
            view=self.view, request=self.request, renderer_classes=[renderer])
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,7 @@ DEFAULTS = {
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    'DEFAULT_PERMISSIONS': (),
 | 
					    'DEFAULT_PERMISSIONS': (),
 | 
				
			||||||
    'DEFAULT_THROTTLES': (),
 | 
					    'DEFAULT_THROTTLES': (),
 | 
				
			||||||
 | 
					    'DEFAULT_CONTENT_NEGOTIATION': 'djangorestframework.contentnegotiation.DefaultContentNegotiation',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
 | 
					    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
 | 
				
			||||||
    'UNAUTHENTICATED_TOKEN': None,
 | 
					    'UNAUTHENTICATED_TOKEN': None,
 | 
				
			||||||
| 
						 | 
					@ -45,7 +46,8 @@ DEFAULTS = {
 | 
				
			||||||
    'FORM_METHOD_OVERRIDE': '_method',
 | 
					    'FORM_METHOD_OVERRIDE': '_method',
 | 
				
			||||||
    'FORM_CONTENT_OVERRIDE': '_content',
 | 
					    'FORM_CONTENT_OVERRIDE': '_content',
 | 
				
			||||||
    'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
 | 
					    'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
 | 
				
			||||||
    'URL_ACCEPT_OVERRIDE': '_accept',
 | 
					    'URL_ACCEPT_OVERRIDE': 'accept',
 | 
				
			||||||
 | 
					    'IGNORE_MSIE_ACCEPT_HEADER': True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'FORMAT_SUFFIX_KWARG': 'format'
 | 
					    'FORMAT_SUFFIX_KWARG': 'format'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -58,8 +60,9 @@ IMPORT_STRINGS = (
 | 
				
			||||||
    'DEFAULT_AUTHENTICATION',
 | 
					    'DEFAULT_AUTHENTICATION',
 | 
				
			||||||
    'DEFAULT_PERMISSIONS',
 | 
					    'DEFAULT_PERMISSIONS',
 | 
				
			||||||
    'DEFAULT_THROTTLES',
 | 
					    'DEFAULT_THROTTLES',
 | 
				
			||||||
 | 
					    'DEFAULT_CONTENT_NEGOTIATION',
 | 
				
			||||||
    'UNAUTHENTICATED_USER',
 | 
					    'UNAUTHENTICATED_USER',
 | 
				
			||||||
    'UNAUTHENTICATED_TOKEN'
 | 
					    'UNAUTHENTICATED_TOKEN',
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,7 +71,7 @@ def perform_import(val, setting):
 | 
				
			||||||
    If the given setting is a string import notation,
 | 
					    If the given setting is a string import notation,
 | 
				
			||||||
    then perform the necessary import or imports.
 | 
					    then perform the necessary import or imports.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    if val is None or setting not in IMPORT_STRINGS:
 | 
					    if val is None or not setting in IMPORT_STRINGS:
 | 
				
			||||||
        return val
 | 
					        return val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if isinstance(val, basestring):
 | 
					    if isinstance(val, basestring):
 | 
				
			||||||
| 
						 | 
					@ -88,10 +91,7 @@ def import_from_string(val, setting):
 | 
				
			||||||
        module_path, class_name = '.'.join(parts[:-1]), parts[-1]
 | 
					        module_path, class_name = '.'.join(parts[:-1]), parts[-1]
 | 
				
			||||||
        module = importlib.import_module(module_path)
 | 
					        module = importlib.import_module(module_path)
 | 
				
			||||||
        return getattr(module, class_name)
 | 
					        return getattr(module, class_name)
 | 
				
			||||||
    except Exception, e:
 | 
					    except:
 | 
				
			||||||
        import traceback
 | 
					 | 
				
			||||||
        tb = traceback.format_exc()
 | 
					 | 
				
			||||||
        import pdb; pdb.set_trace()
 | 
					 | 
				
			||||||
        msg = "Could not import '%s' for API setting '%s'" % (val, setting)
 | 
					        msg = "Could not import '%s' for API setting '%s'" % (val, setting)
 | 
				
			||||||
        raise ImportError(msg)
 | 
					        raise ImportError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ from django.test import TestCase, Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from djangorestframework import status
 | 
					from djangorestframework import status
 | 
				
			||||||
from djangorestframework.authentication import SessionAuthentication
 | 
					from djangorestframework.authentication import SessionAuthentication
 | 
				
			||||||
from djangorestframework.utils import RequestFactory
 | 
					from djangorestframework.compat import RequestFactory
 | 
				
			||||||
from djangorestframework.parsers import (
 | 
					from djangorestframework.parsers import (
 | 
				
			||||||
    FormParser,
 | 
					    FormParser,
 | 
				
			||||||
    MultiPartParser,
 | 
					    MultiPartParser,
 | 
				
			||||||
| 
						 | 
					@ -22,33 +22,21 @@ factory = RequestFactory()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestMethodOverloading(TestCase):
 | 
					class TestMethodOverloading(TestCase):
 | 
				
			||||||
    def test_GET_method(self):
 | 
					    def test_method(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        GET requests identified.
 | 
					        Request methods should be same as underlying request.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = factory.get('/')
 | 
					        request = Request(factory.get('/'))
 | 
				
			||||||
        self.assertEqual(request.method, 'GET')
 | 
					        self.assertEqual(request.method, 'GET')
 | 
				
			||||||
 | 
					        request = Request(factory.post('/'))
 | 
				
			||||||
    def test_POST_method(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        POST requests identified.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        request = factory.post('/')
 | 
					 | 
				
			||||||
        self.assertEqual(request.method, 'POST')
 | 
					        self.assertEqual(request.method, 'POST')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_HEAD_method(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        HEAD requests identified.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        request = factory.head('/')
 | 
					 | 
				
			||||||
        self.assertEqual(request.method, 'HEAD')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_overloaded_method(self):
 | 
					    def test_overloaded_method(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        POST requests can be overloaded to another method by setting a
 | 
					        POST requests can be overloaded to another method by setting a
 | 
				
			||||||
        reserved form field
 | 
					        reserved form field
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = factory.post('/', {Request._METHOD_PARAM: 'DELETE'})
 | 
					        request = Request(factory.post('/', {Request._METHOD_PARAM: 'DELETE'}))
 | 
				
			||||||
        self.assertEqual(request.method, 'DELETE')
 | 
					        self.assertEqual(request.method, 'DELETE')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,14 +45,14 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Ensure request.DATA returns None for GET request with no content.
 | 
					        Ensure request.DATA returns None for GET request with no content.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = factory.get('/')
 | 
					        request = Request(factory.get('/'))
 | 
				
			||||||
        self.assertEqual(request.DATA, None)
 | 
					        self.assertEqual(request.DATA, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_standard_behaviour_determines_no_content_HEAD(self):
 | 
					    def test_standard_behaviour_determines_no_content_HEAD(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Ensure request.DATA returns None for HEAD request.
 | 
					        Ensure request.DATA returns None for HEAD request.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = factory.head('/')
 | 
					        request = Request(factory.head('/'))
 | 
				
			||||||
        self.assertEqual(request.DATA, None)
 | 
					        self.assertEqual(request.DATA, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_standard_behaviour_determines_form_content_POST(self):
 | 
					    def test_standard_behaviour_determines_form_content_POST(self):
 | 
				
			||||||
| 
						 | 
					@ -72,8 +60,8 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
        Ensure request.DATA returns content for POST request with form content.
 | 
					        Ensure request.DATA returns content for POST request with form content.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        data = {'qwerty': 'uiop'}
 | 
					        data = {'qwerty': 'uiop'}
 | 
				
			||||||
        parsers = (FormParser, MultiPartParser)
 | 
					        request = Request(factory.post('/', data))
 | 
				
			||||||
        request = factory.post('/', data, parser=parsers)
 | 
					        request.parser_classes = (FormParser, MultiPartParser)
 | 
				
			||||||
        self.assertEqual(request.DATA.items(), data.items())
 | 
					        self.assertEqual(request.DATA.items(), data.items())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_standard_behaviour_determines_non_form_content_POST(self):
 | 
					    def test_standard_behaviour_determines_non_form_content_POST(self):
 | 
				
			||||||
| 
						 | 
					@ -83,9 +71,8 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        content = 'qwerty'
 | 
					        content = 'qwerty'
 | 
				
			||||||
        content_type = 'text/plain'
 | 
					        content_type = 'text/plain'
 | 
				
			||||||
        parsers = (PlainTextParser,)
 | 
					        request = Request(factory.post('/', content, content_type=content_type))
 | 
				
			||||||
        request = factory.post('/', content, content_type=content_type,
 | 
					        request.parser_classes = (PlainTextParser,)
 | 
				
			||||||
                               parsers=parsers)
 | 
					 | 
				
			||||||
        self.assertEqual(request.DATA, content)
 | 
					        self.assertEqual(request.DATA, content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_standard_behaviour_determines_form_content_PUT(self):
 | 
					    def test_standard_behaviour_determines_form_content_PUT(self):
 | 
				
			||||||
| 
						 | 
					@ -93,17 +80,17 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
        Ensure request.DATA returns content for PUT request with form content.
 | 
					        Ensure request.DATA returns content for PUT request with form content.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        data = {'qwerty': 'uiop'}
 | 
					        data = {'qwerty': 'uiop'}
 | 
				
			||||||
        parsers = (FormParser, MultiPartParser)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        from django import VERSION
 | 
					        from django import VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if VERSION >= (1, 5):
 | 
					        if VERSION >= (1, 5):
 | 
				
			||||||
            from django.test.client import MULTIPART_CONTENT, BOUNDARY, encode_multipart
 | 
					            from django.test.client import MULTIPART_CONTENT, BOUNDARY, encode_multipart
 | 
				
			||||||
            request = factory.put('/', encode_multipart(BOUNDARY, data), parsers=parsers,
 | 
					            request = Request(factory.put('/', encode_multipart(BOUNDARY, data),
 | 
				
			||||||
                                  content_type=MULTIPART_CONTENT)
 | 
					                                  content_type=MULTIPART_CONTENT))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            request = factory.put('/', data, parsers=parsers)
 | 
					            request = Request(factory.put('/', data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        request.parser_classes = (FormParser, MultiPartParser)
 | 
				
			||||||
        self.assertEqual(request.DATA.items(), data.items())
 | 
					        self.assertEqual(request.DATA.items(), data.items())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_standard_behaviour_determines_non_form_content_PUT(self):
 | 
					    def test_standard_behaviour_determines_non_form_content_PUT(self):
 | 
				
			||||||
| 
						 | 
					@ -113,9 +100,8 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        content = 'qwerty'
 | 
					        content = 'qwerty'
 | 
				
			||||||
        content_type = 'text/plain'
 | 
					        content_type = 'text/plain'
 | 
				
			||||||
        parsers = (PlainTextParser, )
 | 
					        request = Request(factory.put('/', content, content_type=content_type))
 | 
				
			||||||
        request = factory.put('/', content, content_type=content_type,
 | 
					        request.parser_classes = (PlainTextParser, )
 | 
				
			||||||
                              parsers=parsers)
 | 
					 | 
				
			||||||
        self.assertEqual(request.DATA, content)
 | 
					        self.assertEqual(request.DATA, content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_overloaded_behaviour_allows_content_tunnelling(self):
 | 
					    def test_overloaded_behaviour_allows_content_tunnelling(self):
 | 
				
			||||||
| 
						 | 
					@ -128,8 +114,8 @@ class TestContentParsing(TestCase):
 | 
				
			||||||
            Request._CONTENT_PARAM: content,
 | 
					            Request._CONTENT_PARAM: content,
 | 
				
			||||||
            Request._CONTENTTYPE_PARAM: content_type
 | 
					            Request._CONTENTTYPE_PARAM: content_type
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        parsers = (PlainTextParser, )
 | 
					        request = Request(factory.post('/', data))
 | 
				
			||||||
        request = factory.post('/', data, parsers=parsers)
 | 
					        request.parser_classes = (PlainTextParser, )
 | 
				
			||||||
        self.assertEqual(request.DATA, content)
 | 
					        self.assertEqual(request.DATA, content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # def test_accessing_post_after_data_form(self):
 | 
					    # def test_accessing_post_after_data_form(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,10 +4,10 @@ import unittest
 | 
				
			||||||
from django.conf.urls.defaults import patterns, url, include
 | 
					from django.conf.urls.defaults import patterns, url, include
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from djangorestframework.response import Response, NotAcceptable
 | 
					from djangorestframework.response import Response
 | 
				
			||||||
from djangorestframework.views import APIView
 | 
					from djangorestframework.views import APIView
 | 
				
			||||||
from djangorestframework.compat import RequestFactory
 | 
					from djangorestframework.compat import RequestFactory
 | 
				
			||||||
from djangorestframework import status
 | 
					from djangorestframework import status, exceptions
 | 
				
			||||||
from djangorestframework.renderers import (
 | 
					from djangorestframework.renderers import (
 | 
				
			||||||
    BaseRenderer,
 | 
					    BaseRenderer,
 | 
				
			||||||
    JSONRenderer,
 | 
					    JSONRenderer,
 | 
				
			||||||
| 
						 | 
					@ -91,7 +91,7 @@ class TestResponseDetermineRenderer(TestCase):
 | 
				
			||||||
        accept_list = ['application/json']
 | 
					        accept_list = ['application/json']
 | 
				
			||||||
        renderer_classes = (MockPickleRenderer, )
 | 
					        renderer_classes = (MockPickleRenderer, )
 | 
				
			||||||
        response = self.get_response(accept_list=accept_list, renderer_classes=renderer_classes)
 | 
					        response = self.get_response(accept_list=accept_list, renderer_classes=renderer_classes)
 | 
				
			||||||
        self.assertRaises(NotAcceptable, response._determine_renderer)
 | 
					        self.assertRaises(exceptions.NotAcceptable, response._determine_renderer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestResponseRenderContent(TestCase):
 | 
					class TestResponseRenderContent(TestCase):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,6 @@
 | 
				
			||||||
from django.utils.encoding import smart_unicode
 | 
					from django.utils.encoding import smart_unicode
 | 
				
			||||||
from django.utils.xmlutils import SimplerXMLGenerator
 | 
					from django.utils.xmlutils import SimplerXMLGenerator
 | 
				
			||||||
 | 
					 | 
				
			||||||
from djangorestframework.compat import StringIO
 | 
					from djangorestframework.compat import StringIO
 | 
				
			||||||
from djangorestframework.compat import RequestFactory as DjangoRequestFactory
 | 
					 | 
				
			||||||
from djangorestframework.request import Request
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import xml.etree.ElementTree as ET
 | 
					import xml.etree.ElementTree as ET
 | 
				
			||||||
| 
						 | 
					@ -102,38 +99,3 @@ class XMLRenderer():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def dict2xml(input):
 | 
					def dict2xml(input):
 | 
				
			||||||
    return XMLRenderer().dict2xml(input)
 | 
					    return XMLRenderer().dict2xml(input)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class RequestFactory(DjangoRequestFactory):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Replicate RequestFactory, but return Request, not HttpRequest.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def get(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).get(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def post(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).post(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def put(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).put(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def delete(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).delete(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def head(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).head(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def options(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        parsers = kwargs.pop('parsers', None)
 | 
					 | 
				
			||||||
        request = super(RequestFactory, self).options(*args, **kwargs)
 | 
					 | 
				
			||||||
        return Request(request, parsers)
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,11 +54,14 @@ def _camelcase_to_spaces(content):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class APIView(_View):
 | 
					class APIView(_View):
 | 
				
			||||||
 | 
					    settings = api_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderer_classes = api_settings.DEFAULT_RENDERERS
 | 
					    renderer_classes = api_settings.DEFAULT_RENDERERS
 | 
				
			||||||
    parser_classes = api_settings.DEFAULT_PARSERS
 | 
					    parser_classes = api_settings.DEFAULT_PARSERS
 | 
				
			||||||
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION
 | 
					    authentication_classes = api_settings.DEFAULT_AUTHENTICATION
 | 
				
			||||||
    throttle_classes = api_settings.DEFAULT_THROTTLES
 | 
					    throttle_classes = api_settings.DEFAULT_THROTTLES
 | 
				
			||||||
    permission_classes = api_settings.DEFAULT_PERMISSIONS
 | 
					    permission_classes = api_settings.DEFAULT_PERMISSIONS
 | 
				
			||||||
 | 
					    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def as_view(cls, **initkwargs):
 | 
					    def as_view(cls, **initkwargs):
 | 
				
			||||||
| 
						 | 
					@ -169,6 +172,27 @@ class APIView(_View):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.renderer_classes[0]
 | 
					        return self.renderer_classes[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_format_suffix(self, **kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Determine if the request includes a '.json' style format suffix
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.settings.FORMAT_SUFFIX_KWARG:
 | 
				
			||||||
 | 
					            return kwargs.get(self.settings.FORMAT_SUFFIX_KWARG)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_renderers(self, format=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Instantiates and returns the list of renderers that this view can use.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return [renderer(self) for renderer in self.renderer_classes]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filter_renderers(self, renderers, format=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        If format suffix such as '.json' is supplied, filter the
 | 
				
			||||||
 | 
					        list of valid renderers for this request.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return [renderer for renderer in renderers
 | 
				
			||||||
 | 
					                if renderer.can_handle_format(format)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_permissions(self):
 | 
					    def get_permissions(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Instantiates and returns the list of permissions that this view requires.
 | 
					        Instantiates and returns the list of permissions that this view requires.
 | 
				
			||||||
| 
						 | 
					@ -177,10 +201,28 @@ class APIView(_View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_throttles(self):
 | 
					    def get_throttles(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Instantiates and returns the list of thottles that this view requires.
 | 
					        Instantiates and returns the list of thottles that this view uses.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return [throttle(self) for throttle in self.throttle_classes]
 | 
					        return [throttle(self) for throttle in self.throttle_classes]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def content_negotiation(self, request):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Determine which renderer and media type to use render the response.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        renderers = self.get_renderers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.format:
 | 
				
			||||||
 | 
					            # If there is a '.json' style format suffix, only use
 | 
				
			||||||
 | 
					            # renderers that accept that format.
 | 
				
			||||||
 | 
					            fallback = renderers[0]
 | 
				
			||||||
 | 
					            renderers = self.filter_renderers(renderers, self.format)
 | 
				
			||||||
 | 
					            if not renderers:
 | 
				
			||||||
 | 
					                self.format404 = True
 | 
				
			||||||
 | 
					                return (fallback, fallback.media_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        conneg = self.content_negotiation_class()
 | 
				
			||||||
 | 
					        return conneg.negotiate(request, renderers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def check_permissions(self, request, obj=None):
 | 
					    def check_permissions(self, request, obj=None):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Check if request should be permitted.
 | 
					        Check if request should be permitted.
 | 
				
			||||||
| 
						 | 
					@ -204,35 +246,43 @@ class APIView(_View):
 | 
				
			||||||
        return Request(request, parser_classes=self.parser_classes,
 | 
					        return Request(request, parser_classes=self.parser_classes,
 | 
				
			||||||
                       authentication_classes=self.authentication_classes)
 | 
					                       authentication_classes=self.authentication_classes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initial(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Runs anything that needs to occur prior to calling the method handlers.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.format = self.get_format_suffix(**kwargs)
 | 
				
			||||||
 | 
					        self.renderer, self.media_type = self.content_negotiation(request)
 | 
				
			||||||
 | 
					        self.check_permissions(request)
 | 
				
			||||||
 | 
					        self.check_throttles(request)
 | 
				
			||||||
 | 
					        # If the request included a non-existant .format URL suffix,
 | 
				
			||||||
 | 
					        # raise 404, but only after first making permission checks.
 | 
				
			||||||
 | 
					        if getattr(self, 'format404', None):
 | 
				
			||||||
 | 
					            raise Http404()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def finalize_response(self, request, response, *args, **kwargs):
 | 
					    def finalize_response(self, request, response, *args, **kwargs):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Returns the final response object.
 | 
					        Returns the final response object.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if isinstance(response, Response):
 | 
					        if isinstance(response, Response):
 | 
				
			||||||
            response.view = self
 | 
					            response.renderer = self.renderer
 | 
				
			||||||
            response.request = request
 | 
					            response.media_type = self.media_type
 | 
				
			||||||
            response.renderer_classes = self.renderer_classes
 | 
					 | 
				
			||||||
            if api_settings.FORMAT_SUFFIX_KWARG:
 | 
					 | 
				
			||||||
                response.format = kwargs.get(api_settings.FORMAT_SUFFIX_KWARG, None)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key, value in self.headers.items():
 | 
					        for key, value in self.headers.items():
 | 
				
			||||||
            response[key] = value
 | 
					            response[key] = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response
 | 
					        return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initial(self, request, *args, **kwargs):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Runs anything that needs to occur prior to calling the method handlers.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.check_permissions(request)
 | 
					 | 
				
			||||||
        self.check_throttles(request)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle_exception(self, exc):
 | 
					    def handle_exception(self, exc):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Handle any exception that occurs, by returning an appropriate response,
 | 
					        Handle any exception that occurs, by returning an appropriate response,
 | 
				
			||||||
        or re-raising the error.
 | 
					        or re-raising the error.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if isinstance(exc, exceptions.Throttled):
 | 
					        if isinstance(exc, exceptions.NotAcceptable):
 | 
				
			||||||
 | 
					            # Fall back to default renderer
 | 
				
			||||||
 | 
					            self.renderer = exc.available_renderers[0]
 | 
				
			||||||
 | 
					            self.media_type = exc.available_renderers[0].media_type
 | 
				
			||||||
 | 
					        elif isinstance(exc, exceptions.Throttled):
 | 
				
			||||||
 | 
					            # Throttle wait header
 | 
				
			||||||
            self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait
 | 
					            self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if isinstance(exc, exceptions.APIException):
 | 
					        if isinstance(exc, exceptions.APIException):
 | 
				
			||||||
| 
						 | 
					@ -250,14 +300,8 @@ class APIView(_View):
 | 
				
			||||||
    @csrf_exempt
 | 
					    @csrf_exempt
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        `APIView.dispatch()` is pretty much the same as Django's regular
 | 
					        `.dispatch()` is pretty much the same as Django's regular dispatch,
 | 
				
			||||||
        `View.dispatch()`, except that it includes hooks to:
 | 
					        but with extra hooks for startup, finalize, and exception handling.
 | 
				
			||||||
 | 
					 | 
				
			||||||
        * Initialize the request object.
 | 
					 | 
				
			||||||
        * Finalize the response object.
 | 
					 | 
				
			||||||
        * Handle exceptions that occur in the handler method.
 | 
					 | 
				
			||||||
        * An initial hook for code such as permission checking that should
 | 
					 | 
				
			||||||
          occur prior to running the method handlers.
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        request = self.initialize_request(request, *args, **kwargs)
 | 
					        request = self.initialize_request(request, *args, **kwargs)
 | 
				
			||||||
        self.request = request
 | 
					        self.request = request
 | 
				
			||||||
| 
						 | 
					@ -270,7 +314,8 @@ class APIView(_View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Get the appropriate handler method
 | 
					            # Get the appropriate handler method
 | 
				
			||||||
            if request.method.lower() in self.http_method_names:
 | 
					            if request.method.lower() in self.http_method_names:
 | 
				
			||||||
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
 | 
					                handler = getattr(self, request.method.lower(),
 | 
				
			||||||
 | 
					                                  self.http_method_not_allowed)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                handler = self.http_method_not_allowed
 | 
					                handler = self.http_method_not_allowed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user