mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
cleanup
This commit is contained in:
parent
44c8b89c60
commit
8f6bcac7f3
|
@ -46,12 +46,20 @@ class RequestMixin(object):
|
||||||
_CONTENTTYPE_PARAM = '_content_type'
|
_CONTENTTYPE_PARAM = '_content_type'
|
||||||
_CONTENT_PARAM = '_content'
|
_CONTENT_PARAM = '_content'
|
||||||
|
|
||||||
|
"""
|
||||||
|
The set of request parsers that the view can handle.
|
||||||
|
|
||||||
|
Should be a tuple/list of classes as described in the ``parsers`` module.
|
||||||
|
"""
|
||||||
parsers = ()
|
parsers = ()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def method(self):
|
def method(self):
|
||||||
"""
|
"""
|
||||||
Returns the HTTP method.
|
Returns the HTTP method.
|
||||||
|
|
||||||
|
This should be used instead of ``request.method``, as it allows the method
|
||||||
|
to be overridden by using a hidden form field on a form POST request.
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, '_method'):
|
if not hasattr(self, '_method'):
|
||||||
self._load_method_and_content_type()
|
self._load_method_and_content_type()
|
||||||
|
@ -62,6 +70,10 @@ class RequestMixin(object):
|
||||||
def content_type(self):
|
def content_type(self):
|
||||||
"""
|
"""
|
||||||
Returns the content type header.
|
Returns the content type header.
|
||||||
|
|
||||||
|
This should be used instead of ``request.META.get('HTTP_CONTENT_TYPE')``,
|
||||||
|
as it allows the content type to be overridden by using a hidden form
|
||||||
|
field on a form POST request.
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, '_content_type'):
|
if not hasattr(self, '_content_type'):
|
||||||
self._load_method_and_content_type()
|
self._load_method_and_content_type()
|
||||||
|
@ -71,7 +83,10 @@ class RequestMixin(object):
|
||||||
@property
|
@property
|
||||||
def DATA(self):
|
def DATA(self):
|
||||||
"""
|
"""
|
||||||
Returns the request data.
|
Parses the request body and returns the data.
|
||||||
|
|
||||||
|
Similar to ``request.POST``, except that it handles arbitrary parsers,
|
||||||
|
and also works on methods other than POST (eg PUT).
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, '_data'):
|
if not hasattr(self, '_data'):
|
||||||
self._load_data_and_files()
|
self._load_data_and_files()
|
||||||
|
@ -81,7 +96,9 @@ class RequestMixin(object):
|
||||||
@property
|
@property
|
||||||
def FILES(self):
|
def FILES(self):
|
||||||
"""
|
"""
|
||||||
Returns the request files.
|
Parses the request body and returns the files.
|
||||||
|
Similar to request.FILES, except that it handles arbitrary parsers,
|
||||||
|
and also works on methods other than POST (eg PUT).
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, '_files'):
|
if not hasattr(self, '_files'):
|
||||||
self._load_data_and_files()
|
self._load_data_and_files()
|
||||||
|
@ -205,8 +222,14 @@ class ResponseMixin(object):
|
||||||
_ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params
|
_ACCEPT_QUERY_PARAM = '_accept' # Allow override of Accept header in URL query params
|
||||||
_IGNORE_IE_ACCEPT_HEADER = True
|
_IGNORE_IE_ACCEPT_HEADER = True
|
||||||
|
|
||||||
|
"""
|
||||||
|
The set of response renderers that the view can handle.
|
||||||
|
|
||||||
|
Should be a tuple/list of classes as described in the ``renderers`` module.
|
||||||
|
"""
|
||||||
renderers = ()
|
renderers = ()
|
||||||
|
|
||||||
|
|
||||||
# TODO: wrap this behavior around dispatch(), ensuring it works
|
# TODO: wrap this behavior around dispatch(), ensuring it works
|
||||||
# out of the box with existing Django classes that use render_to_response.
|
# out of the box with existing Django classes that use render_to_response.
|
||||||
def render(self, response):
|
def render(self, response):
|
||||||
|
@ -330,14 +353,33 @@ class AuthMixin(object):
|
||||||
"""
|
"""
|
||||||
Simple mixin class to add authentication and permission checking to a ``View`` class.
|
Simple mixin class to add authentication and permission checking to a ``View`` class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The set of authentication types that this view can handle.
|
||||||
|
|
||||||
|
|
||||||
|
Should be a tuple/list of classes as described in the ``authentication`` module.
|
||||||
|
"""
|
||||||
authentication = ()
|
authentication = ()
|
||||||
|
|
||||||
|
"""
|
||||||
|
The set of permissions that will be enforced on this view.
|
||||||
|
|
||||||
|
Should be a tuple/list of classes as described in the ``permissions`` module.
|
||||||
|
"""
|
||||||
permissions = ()
|
permissions = ()
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user(self):
|
def user(self):
|
||||||
|
"""
|
||||||
|
Returns the user for the current request, as determined by the set of
|
||||||
|
authentication classes applied to the ``View``.
|
||||||
|
"""
|
||||||
if not hasattr(self, '_user'):
|
if not hasattr(self, '_user'):
|
||||||
self._user = self._authenticate()
|
self._user = self._authenticate()
|
||||||
return self._user
|
return self._user
|
||||||
|
|
||||||
|
|
||||||
def _authenticate(self):
|
def _authenticate(self):
|
||||||
"""
|
"""
|
||||||
|
@ -351,6 +393,7 @@ class AuthMixin(object):
|
||||||
return user
|
return user
|
||||||
return AnonymousUser()
|
return AnonymousUser()
|
||||||
|
|
||||||
|
|
||||||
# TODO: wrap this behavior around dispatch()
|
# TODO: wrap this behavior around dispatch()
|
||||||
def _check_permissions(self):
|
def _check_permissions(self):
|
||||||
"""
|
"""
|
||||||
|
@ -359,7 +402,7 @@ class AuthMixin(object):
|
||||||
user = self.user
|
user = self.user
|
||||||
for permission_cls in self.permissions:
|
for permission_cls in self.permissions:
|
||||||
permission = permission_cls(self)
|
permission = permission_cls(self)
|
||||||
permission.check_permission(user)
|
permission.check_permission(user)
|
||||||
|
|
||||||
|
|
||||||
########## Resource Mixin ##########
|
########## Resource Mixin ##########
|
||||||
|
|
|
@ -111,7 +111,8 @@ class PlainTextParser(BaseParser):
|
||||||
|
|
||||||
|
|
||||||
class FormParser(BaseParser, DataFlatener):
|
class FormParser(BaseParser, DataFlatener):
|
||||||
"""The default parser for form data.
|
"""
|
||||||
|
The default parser for form data.
|
||||||
Return a dict containing a single value for each non-reserved parameter.
|
Return a dict containing a single value for each non-reserved parameter.
|
||||||
|
|
||||||
In order to handle select multiple (and having possibly more than a single value for each parameter),
|
In order to handle select multiple (and having possibly more than a single value for each parameter),
|
||||||
|
@ -122,7 +123,8 @@ class FormParser(BaseParser, DataFlatener):
|
||||||
"""The value of the parameter when the select multiple is empty.
|
"""The value of the parameter when the select multiple is empty.
|
||||||
Browsers are usually stripping the select multiple that have no option selected from the parameters sent.
|
Browsers are usually stripping the select multiple that have no option selected from the parameters sent.
|
||||||
A common hack to avoid this is to send the parameter with a value specifying that the list is empty.
|
A common hack to avoid this is to send the parameter with a value specifying that the list is empty.
|
||||||
This value will always be stripped before the data is returned."""
|
This value will always be stripped before the data is returned.
|
||||||
|
"""
|
||||||
EMPTY_VALUE = '_empty'
|
EMPTY_VALUE = '_empty'
|
||||||
RESERVED_FORM_PARAMS = ('csrfmiddlewaretoken',)
|
RESERVED_FORM_PARAMS = ('csrfmiddlewaretoken',)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ and providing forms and links depending on the allowed methods, renderers and pa
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.serializers.json import DateTimeAwareJSONEncoder
|
||||||
from django.template import RequestContext, loader
|
from django.template import RequestContext, loader
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ class JSONRenderer(BaseRenderer):
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
indent = None
|
indent = None
|
||||||
|
|
||||||
return json.dumps(obj, indent=indent, sort_keys=sort_keys)
|
return json.dumps(obj, cls=DateTimeAwareJSONEncoder, indent=indent, sort_keys=sort_keys)
|
||||||
|
|
||||||
|
|
||||||
class XMLRenderer(BaseRenderer):
|
class XMLRenderer(BaseRenderer):
|
||||||
|
|
|
@ -16,9 +16,14 @@ def _model_to_dict(instance, fields=None, exclude=None):
|
||||||
"""
|
"""
|
||||||
opts = instance._meta
|
opts = instance._meta
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
|
#print [rel.name for rel in opts.get_all_related_objects()]
|
||||||
|
#related = [rel.get_accessor_name() for rel in opts.get_all_related_objects()]
|
||||||
|
#print [getattr(instance, rel) for rel in related]
|
||||||
|
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
if not f.editable:
|
#if not f.editable:
|
||||||
continue
|
# continue
|
||||||
if fields and not f.name in fields:
|
if fields and not f.name in fields:
|
||||||
continue
|
continue
|
||||||
if exclude and f.name in exclude:
|
if exclude and f.name in exclude:
|
||||||
|
@ -27,6 +32,15 @@ def _model_to_dict(instance, fields=None, exclude=None):
|
||||||
data[f.name] = getattr(instance, f.name)
|
data[f.name] = getattr(instance, f.name)
|
||||||
else:
|
else:
|
||||||
data[f.name] = f.value_from_object(instance)
|
data[f.name] = f.value_from_object(instance)
|
||||||
|
|
||||||
|
#print fields - (opts.fields + opts.many_to_many)
|
||||||
|
#for related in [rel.get_accessor_name() for rel in opts.get_all_related_objects()]:
|
||||||
|
# if fields and not related in fields:
|
||||||
|
# continue
|
||||||
|
# if exclude and related in exclude:
|
||||||
|
# continue
|
||||||
|
# data[related] = getattr(instance, related)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +141,8 @@ class Resource(BaseResource):
|
||||||
A (horrible) munging of Piston's pre-serialization. Returns a dict.
|
A (horrible) munging of Piston's pre-serialization. Returns a dict.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
return _object_to_data(obj)
|
||||||
|
|
||||||
def _any(thing, fields=()):
|
def _any(thing, fields=()):
|
||||||
"""
|
"""
|
||||||
Dispatch, all types are routed through here.
|
Dispatch, all types are routed through here.
|
|
@ -1,6 +1,6 @@
|
||||||
"""Tests for the resource module"""
|
"""Tests for the resource module"""
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from djangorestframework.resource import _object_to_data
|
from djangorestframework.resources import _object_to_data
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
|
@ -56,9 +56,10 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
|
||||||
"""
|
"""
|
||||||
return [method.upper() for method in self.http_method_names if hasattr(self, method)]
|
return [method.upper() for method in self.http_method_names if hasattr(self, method)]
|
||||||
|
|
||||||
|
|
||||||
def http_method_not_allowed(self, request, *args, **kwargs):
|
def http_method_not_allowed(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return an HTTP 405 error if an operation is called which does not have a handler method.
|
Return an HTTP 405 error if an operation is called which does not have a handler method.
|
||||||
"""
|
"""
|
||||||
raise ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED,
|
raise ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED,
|
||||||
{'detail': 'Method \'%s\' not allowed on this resource.' % self.method})
|
{'detail': 'Method \'%s\' not allowed on this resource.' % self.method})
|
||||||
|
@ -68,56 +69,48 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
|
||||||
# all other authentication is CSRF exempt.
|
# all other authentication is CSRF exempt.
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
try:
|
self.request = request
|
||||||
self.request = request
|
self.args = args
|
||||||
self.args = args
|
self.kwargs = kwargs
|
||||||
self.kwargs = kwargs
|
|
||||||
|
# Calls to 'reverse' will not be fully qualified unless we set the scheme/host/port here.
|
||||||
|
prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
|
||||||
|
set_script_prefix(prefix)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Authenticate and check request is 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 Response, or an object, or None
|
||||||
|
if 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.object_to_data(response.raw_content)
|
||||||
|
|
||||||
# Calls to 'reverse' will not be fully qualified unless we set the scheme/host/port here.
|
except ErrorResponse, exc:
|
||||||
prefix = '%s://%s' % (request.is_secure() and 'https' or 'http', request.get_host())
|
response = exc.response
|
||||||
set_script_prefix(prefix)
|
|
||||||
|
# Always add these headers.
|
||||||
try:
|
#
|
||||||
# Authenticate and check request is has the relevant permissions
|
# TODO - this isn't actually the correct way to set the vary header,
|
||||||
self._check_permissions()
|
# also it's currently sub-obtimal for HTTP caching - need to sort that out.
|
||||||
|
response.headers['Allow'] = ', '.join(self.allowed_methods)
|
||||||
# Get the appropriate handler method
|
response.headers['Vary'] = 'Authenticate, Accept'
|
||||||
if self.method.lower() in self.http_method_names:
|
|
||||||
handler = getattr(self, self.method.lower(), self.http_method_not_allowed)
|
return self.render(response)
|
||||||
else:
|
|
||||||
handler = self.http_method_not_allowed
|
|
||||||
|
|
||||||
response_obj = handler(request, *args, **kwargs)
|
|
||||||
|
|
||||||
# Allow return value to be either Response, or an object, or None
|
|
||||||
if 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.object_to_data(response.raw_content)
|
|
||||||
|
|
||||||
except ErrorResponse, exc:
|
|
||||||
response = exc.response
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Always add these headers.
|
|
||||||
#
|
|
||||||
# TODO - this isn't actually the correct way to set the vary header,
|
|
||||||
# also it's currently sub-obtimal for HTTP caching - need to sort that out.
|
|
||||||
response.headers['Allow'] = ', '.join(self.allowed_methods)
|
|
||||||
response.headers['Vary'] = 'Authenticate, Accept'
|
|
||||||
|
|
||||||
return self.render(response)
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user