aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework
diff options
context:
space:
mode:
authorTom Christie2011-05-13 09:59:36 +0100
committerTom Christie2011-05-13 09:59:36 +0100
commit8f6bcac7f3f156831343cc7fec79f624dcc2639f (patch)
treeea5017ba47fdbd4410e768c5851a098eed0045d5 /djangorestframework
parent44c8b89c6051483677e72a6fc657b1e0457182d1 (diff)
downloaddjango-rest-framework-8f6bcac7f3f156831343cc7fec79f624dcc2639f.tar.bz2
cleanup
Diffstat (limited to 'djangorestframework')
-rw-r--r--djangorestframework/mixins.py51
-rw-r--r--djangorestframework/parsers.py6
-rw-r--r--djangorestframework/renderers.py3
-rw-r--r--djangorestframework/resources.py (renamed from djangorestframework/resource.py)20
-rw-r--r--djangorestframework/tests/resources.py (renamed from djangorestframework/tests/resource.py)2
-rw-r--r--djangorestframework/views.py93
6 files changed, 115 insertions, 60 deletions
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index 524e9268..12f2d779 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -46,12 +46,20 @@ class RequestMixin(object):
_CONTENTTYPE_PARAM = '_content_type'
_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 = ()
@property
def method(self):
"""
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'):
self._load_method_and_content_type()
@@ -62,6 +70,10 @@ class RequestMixin(object):
def content_type(self):
"""
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'):
self._load_method_and_content_type()
@@ -71,7 +83,10 @@ class RequestMixin(object):
@property
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'):
self._load_data_and_files()
@@ -81,7 +96,9 @@ class RequestMixin(object):
@property
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'):
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
_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 = ()
-
+
+
# TODO: wrap this behavior around dispatch(), ensuring it works
# out of the box with existing Django classes that use render_to_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.
"""
+
+ """
+ The set of authentication types that this view can handle.
+
+
+ Should be a tuple/list of classes as described in the ``authentication`` module.
+ """
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 = ()
+
@property
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'):
self._user = self._authenticate()
return self._user
+
def _authenticate(self):
"""
@@ -351,6 +393,7 @@ class AuthMixin(object):
return user
return AnonymousUser()
+
# TODO: wrap this behavior around dispatch()
def _check_permissions(self):
"""
@@ -359,7 +402,7 @@ class AuthMixin(object):
user = self.user
for permission_cls in self.permissions:
permission = permission_cls(self)
- permission.check_permission(user)
+ permission.check_permission(user)
########## Resource Mixin ##########
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
index 9e1b971b..4337098a 100644
--- a/djangorestframework/parsers.py
+++ b/djangorestframework/parsers.py
@@ -111,7 +111,8 @@ class PlainTextParser(BaseParser):
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.
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.
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.
- 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'
RESERVED_FORM_PARAMS = ('csrfmiddlewaretoken',)
diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py
index e8763f34..371b5ef0 100644
--- a/djangorestframework/renderers.py
+++ b/djangorestframework/renderers.py
@@ -7,6 +7,7 @@ and providing forms and links depending on the allowed methods, renderers and pa
"""
from django import forms
from django.conf import settings
+from django.core.serializers.json import DateTimeAwareJSONEncoder
from django.template import RequestContext, loader
from django.utils import simplejson as json
@@ -81,7 +82,7 @@ class JSONRenderer(BaseRenderer):
except (ValueError, TypeError):
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):
diff --git a/djangorestframework/resource.py b/djangorestframework/resources.py
index 775d5288..f47b41d0 100644
--- a/djangorestframework/resource.py
+++ b/djangorestframework/resources.py
@@ -16,9 +16,14 @@ def _model_to_dict(instance, fields=None, exclude=None):
"""
opts = instance._meta
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:
- if not f.editable:
- continue
+ #if not f.editable:
+ # continue
if fields and not f.name in fields:
continue
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)
else:
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
@@ -127,6 +141,8 @@ class Resource(BaseResource):
A (horrible) munging of Piston's pre-serialization. Returns a dict.
"""
+ return _object_to_data(obj)
+
def _any(thing, fields=()):
"""
Dispatch, all types are routed through here.
diff --git a/djangorestframework/tests/resource.py b/djangorestframework/tests/resources.py
index 0ed41951..6aa569d3 100644
--- a/djangorestframework/tests/resource.py
+++ b/djangorestframework/tests/resources.py
@@ -1,6 +1,6 @@
"""Tests for the resource module"""
from django.test import TestCase
-from djangorestframework.resource import _object_to_data
+from djangorestframework.resources import _object_to_data
import datetime
import decimal
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 211dafca..315c25a9 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -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)]
+
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,
{'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.
@csrf_exempt
def dispatch(self, request, *args, **kwargs):
- try:
- self.request = request
- self.args = args
- 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)
+ self.request = request
+ self.args = args
+ 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)
- # 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()
+ except ErrorResponse, exc:
+ response = exc.response
+
+ # 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)