This commit is contained in:
Tom Christie 2011-05-13 09:59:36 +01:00
parent 44c8b89c60
commit 8f6bcac7f3
6 changed files with 115 additions and 60 deletions

View File

@ -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,15 +353,34 @@ 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):
"""
Attempt to authenticate the request using each authentication class in turn.
@ -351,6 +393,7 @@ class AuthMixin(object):
return user
return AnonymousUser()
# TODO: wrap this behavior around dispatch()
def _check_permissions(self):
"""

View File

@ -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',)

View File

@ -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):

View File

@ -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.

View File

@ -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

View File

@ -56,6 +56,7 @@ 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.
@ -68,7 +69,6 @@ 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
@ -102,10 +102,6 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
except ErrorResponse, exc:
response = exc.response
except:
import traceback
traceback.print_exc()
raise
# Always add these headers.
#
@ -115,9 +111,6 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View):
response.headers['Vary'] = 'Authenticate, Accept'
return self.render(response)
except:
import traceback
traceback.print_exc()