aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework/views.py')
-rw-r--r--rest_framework/views.py65
1 files changed, 55 insertions, 10 deletions
diff --git a/rest_framework/views.py b/rest_framework/views.py
index bc870417..b4abc4d9 100644
--- a/rest_framework/views.py
+++ b/rest_framework/views.py
@@ -2,10 +2,11 @@
Provides an APIView class that is the base of all views in REST framework.
"""
from __future__ import unicode_literals
-
from django.core.exceptions import PermissionDenied
from django.http import Http404
+from django.utils import six
from django.utils.encoding import smart_text
+from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status, exceptions
from rest_framework.compat import HttpResponseBase, View
@@ -13,6 +14,8 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.utils import formatting
+import inspect
+import warnings
def get_view_name(view_cls, suffix=None):
@@ -46,7 +49,7 @@ def get_view_description(view_cls, html=False):
return description
-def exception_handler(exc):
+def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
@@ -72,11 +75,13 @@ def exception_handler(exc):
return Response(data, status=exc.status_code, headers=headers)
elif isinstance(exc, Http404):
- data = {'detail': 'Not found'}
+ msg = _('Not found.')
+ data = {'detail': six.text_type(msg)}
return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, PermissionDenied):
- data = {'detail': 'Permission denied'}
+ msg = _('Permission denied.')
+ data = {'detail': six.text_type(msg)}
return Response(data, status=status.HTTP_403_FORBIDDEN)
# Note: Unhandled exceptions will raise a 500 error.
@@ -93,6 +98,7 @@ class APIView(View):
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
+ versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
@@ -184,6 +190,18 @@ class APIView(View):
'request': getattr(self, 'request', None)
}
+ def get_exception_handler_context(self):
+ """
+ Returns a dict that is passed through to EXCEPTION_HANDLER,
+ as the `context` argument.
+ """
+ return {
+ 'view': self,
+ 'args': getattr(self, 'args', ()),
+ 'kwargs': getattr(self, 'kwargs', {}),
+ 'request': getattr(self, 'request', None)
+ }
+
def get_view_name(self):
"""
Return the view name, as used in OPTIONS responses and in the
@@ -300,6 +318,16 @@ class APIView(View):
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
+ def determine_version(self, request, *args, **kwargs):
+ """
+ If versioning is being used, then determine any API version for the
+ incoming request. Returns a two-tuple of (version, versioning_scheme)
+ """
+ if self.versioning_class is None:
+ return (None, None)
+ scheme = self.versioning_class()
+ return (scheme.determine_version(request, *args, **kwargs), scheme)
+
# Dispatch methods
def initialize_request(self, request, *args, **kwargs):
@@ -308,11 +336,13 @@ class APIView(View):
"""
parser_context = self.get_parser_context(request)
- return Request(request,
- parsers=self.get_parsers(),
- authenticators=self.get_authenticators(),
- negotiator=self.get_content_negotiator(),
- parser_context=parser_context)
+ return Request(
+ request,
+ parsers=self.get_parsers(),
+ authenticators=self.get_authenticators(),
+ negotiator=self.get_content_negotiator(),
+ parser_context=parser_context
+ )
def initial(self, request, *args, **kwargs):
"""
@@ -329,6 +359,10 @@ class APIView(View):
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
+ # Determine the API version, if versioning is in use.
+ version, scheme = self.determine_version(request, *args, **kwargs)
+ request.version, request.versioning_scheme = version, scheme
+
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
@@ -369,7 +403,18 @@ class APIView(View):
else:
exc.status_code = status.HTTP_403_FORBIDDEN
- response = self.settings.EXCEPTION_HANDLER(exc)
+ exception_handler = self.settings.EXCEPTION_HANDLER
+
+ if len(inspect.getargspec(exception_handler).args) == 1:
+ warnings.warn(
+ 'The `exception_handler(exc)` call signature is deprecated. '
+ 'Use `exception_handler(exc, context) instead.',
+ DeprecationWarning
+ )
+ response = exception_handler(exc)
+ else:
+ context = self.get_exception_handler_context()
+ response = exception_handler(exc, context)
if response is None:
raise