django-rest-framework/djangorestframework/views.py

205 lines
7.3 KiB
Python
Raw Normal View History

"""
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.
"""
from django.core.urlresolvers import set_script_prefix, get_script_prefix
from django.http import HttpResponse
2011-05-04 12:21:17 +04:00
from django.views.decorators.csrf import csrf_exempt
from djangorestframework.compat import View as DjangoView
2011-05-04 12:21:17 +04:00
from djangorestframework.response import Response, ErrorResponse
from djangorestframework.mixins import *
2011-05-12 18:11:14 +04:00
from djangorestframework import resources, renderers, parsers, authentication, permissions, status
from djangorestframework.utils.description import get_name, get_description
2011-05-04 12:21:17 +04:00
2011-05-10 15:21:48 +04:00
__all__ = (
'View',
2011-05-10 15:21:48 +04:00
'ModelView',
'InstanceModelView',
'ListModelView',
2011-05-10 15:21:48 +04:00
'ListOrCreateModelView'
)
2011-05-04 12:21:17 +04:00
class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
Handles incoming requests and maps them to REST operations.
Performs request deserialization, response serialization, authentication and input validation.
"""
2011-05-04 12:21:17 +04:00
"""
The resource to use when validating requests and filtering responses,
or `None` to use default behaviour.
"""
resource = None
2011-05-04 12:21:17 +04:00
"""
List of renderers the resource can serialize the response with, ordered by preference.
"""
2011-07-22 15:01:38 +04:00
renderers = renderers.DEFAULT_RENDERERS
2011-12-09 16:54:11 +04:00
"""
List of parsers the resource can parse the request with.
"""
parsers = parsers.DEFAULT_PARSERS
2011-05-04 12:21:17 +04:00
"""
List of all authenticating methods to attempt.
"""
2011-06-07 17:12:02 +04:00
authentication = ( authentication.UserLoggedInAuthentication,
authentication.BasicAuthentication )
2011-12-09 16:54:11 +04:00
"""
List of all permissions that must be checked.
"""
2011-05-04 12:21:17 +04:00
permissions = ( permissions.FullAnonAccess, )
2011-12-09 16:54:11 +04:00
2011-05-23 20:07:31 +04:00
@classmethod
def as_view(cls, **initkwargs):
"""
Override the default :meth:`as_view` to store an instance of the view
as an attribute on the callable function. This allows us to discover
2011-12-09 16:54:11 +04:00
information about the view when we do URL reverse lookups.
2011-05-23 20:07:31 +04:00
"""
view = super(View, cls).as_view(**initkwargs)
2011-05-23 20:07:31 +04:00
view.cls_instance = cls(**initkwargs)
return view
2011-05-04 12:21:17 +04:00
@property
def allowed_methods(self):
2011-05-12 18:11:14 +04:00
"""
Return the list of allowed HTTP methods, uppercased.
"""
2011-05-04 12:21:17 +04:00
return [method.upper() for method in self.http_method_names if hasattr(self, method)]
def http_method_not_allowed(self, request, *args, **kwargs):
"""
2011-12-09 16:54:11 +04:00
Return an HTTP 405 error if an operation is called which does not have a handler method.
"""
2011-05-04 12:21:17 +04:00
raise ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED,
2011-05-12 18:11:14 +04:00
{'detail': 'Method \'%s\' not allowed on this resource.' % self.method})
2011-05-04 12:21:17 +04:00
def initial(self, request, *args, **kargs):
"""
Hook for any code that needs to run prior to anything else.
Required if you want to do things like set `request.upload_handlers` before
the authentication and dispatch handling is run.
"""
2012-01-11 18:42:16 +04:00
# Calls to 'reverse' will not be fully qualified unless we set the
# scheme/host/port here.
self.orig_prefix = get_script_prefix()
if not (self.orig_prefix.startswith('http:') or self.orig_prefix.startswith('https:')):
prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
set_script_prefix(prefix + self.orig_prefix)
2012-01-11 18:42:16 +04:00
def final(self, request, response, *args, **kargs):
"""
Hook for any code that needs to run after everything else in the view.
"""
# Restore script_prefix.
set_script_prefix(self.orig_prefix)
# Always add these headers.
response.headers['Allow'] = ', '.join(self.allowed_methods)
# sample to allow caching using Vary http header
response.headers['Vary'] = 'Authenticate, Accept'
# merge with headers possibly set at some point in the view
response.headers.update(self.headers)
return self.render(response)
2011-06-13 22:42:37 +04:00
def add_header(self, field, value):
"""
2011-12-09 16:54:11 +04:00
Add *field* and *value* to the :attr:`headers` attribute of the :class:`View` class.
2011-06-13 22:42:37 +04:00
"""
self.headers[field] = value
2011-05-04 12:21:17 +04:00
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
@csrf_exempt
def dispatch(self, request, *args, **kwargs):
self.request = request
self.args = args
self.kwargs = kwargs
self.headers = {}
try:
self.initial(request, *args, **kwargs)
2011-12-09 16:54:11 +04:00
# Authenticate and check request has the relevant permissions
self._check_permissions()
# Get the appropriate handler method
if self.method.lower() in self.http_method_names:
handler = getattr(self, self.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response_obj = handler(request, *args, **kwargs)
# Allow return value to be either HttpResponse, Response, or an object, or None
if isinstance(response_obj, HttpResponse):
return response_obj
elif isinstance(response_obj, Response):
response = response_obj
elif response_obj is not None:
response = Response(status.HTTP_200_OK, response_obj)
else:
response = Response(status.HTTP_204_NO_CONTENT)
# Pre-serialize filtering (eg filter complex objects into natively serializable types)
response.cleaned_content = self.filter_response(response.raw_content)
2011-12-09 16:54:11 +04:00
except ErrorResponse, exc:
response = exc.response
2011-12-09 16:54:11 +04:00
return self.final(request, response, *args, **kwargs)
2011-12-09 16:54:11 +04:00
def options(self, request, *args, **kwargs):
response_obj = {
2011-12-09 16:54:11 +04:00
'name': get_name(self),
'description': get_description(self),
'renders': self._rendered_media_types,
'parses': self._parsed_media_types,
}
form = self.get_bound_form()
if form is not None:
field_name_types = {}
for name, field in form.fields.iteritems():
field_name_types[name] = field.__class__.__name__
response_obj['fields'] = field_name_types
# Note 'ErrorResponse' is misleading, it's just any response
# that should be rendered and returned immediately, without any
# response filtering.
raise ErrorResponse(status.HTTP_200_OK, response_obj)
2011-05-04 12:21:17 +04:00
class ModelView(View):
2011-07-22 15:03:04 +04:00
"""
A RESTful view that maps to a model in the database.
"""
2011-05-12 18:11:14 +04:00
resource = resources.ModelResource
2011-05-04 12:21:17 +04:00
class InstanceModelView(InstanceMixin, ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView):
2011-07-22 15:03:04 +04:00
"""
A view which provides default operations for read/update/delete against a model instance.
"""
2011-05-23 20:07:31 +04:00
_suffix = 'Instance'
2011-05-04 12:21:17 +04:00
2011-05-10 13:49:28 +04:00
class ListModelView(ListModelMixin, ModelView):
2011-07-22 15:03:04 +04:00
"""
A view which provides default operations for list, against a model in the database.
2011-12-09 16:54:11 +04:00
"""
2011-05-23 20:07:31 +04:00
_suffix = 'List'
2011-05-04 12:21:17 +04:00
2011-05-10 13:49:28 +04:00
class ListOrCreateModelView(ListModelMixin, CreateModelMixin, ModelView):
2011-07-22 15:03:04 +04:00
"""
A view which provides default operations for list and create, against a model in the database.
2011-12-09 16:54:11 +04:00
"""
2011-05-23 20:07:31 +04:00
_suffix = 'List'