mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-09-16 09:12:29 +03:00
Merge 42cda616da
into 365703e1bb
This commit is contained in:
commit
3f4318ab98
|
@ -471,12 +471,21 @@ class ResourceMixin(object):
|
||||||
"""
|
"""
|
||||||
return self.deserialize(self.request.GET)
|
return self.deserialize(self.request.GET)
|
||||||
|
|
||||||
@property
|
def get_resource_class(self):
|
||||||
def resource(self):
|
if self.resource_class:
|
||||||
if not hasattr(self, '_resource'):
|
return self.resource_class
|
||||||
self._resource = self.resource_class(view=self)
|
elif getattr(self, 'model', None):
|
||||||
return self._resource
|
return ModelResource
|
||||||
|
elif getattr(self, 'form', None):
|
||||||
|
return FormResource
|
||||||
|
elif hasattr(self, 'request') and getattr(self, '%s_form' % self.method.lower(), None):
|
||||||
|
return FormResource
|
||||||
|
else:
|
||||||
|
return Resource
|
||||||
|
|
||||||
|
def get_resource(self):
|
||||||
|
resource_class = self.get_resource_class()
|
||||||
|
return resource_class(view=self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,6 +505,7 @@ class InstanceMixin(object):
|
||||||
associated with this view.
|
associated with this view.
|
||||||
"""
|
"""
|
||||||
view = super(InstanceMixin, cls).as_view(**initkwargs)
|
view = super(InstanceMixin, cls).as_view(**initkwargs)
|
||||||
|
# TODO: FIX !!! Very bad now, since this is attached on the class (thread-safety)
|
||||||
resource_class = getattr(cls(**initkwargs), 'resource_class', None)
|
resource_class = getattr(cls(**initkwargs), 'resource_class', None)
|
||||||
if resource_class:
|
if resource_class:
|
||||||
# We do a little dance when we store the view callable...
|
# We do a little dance when we store the view callable...
|
||||||
|
@ -512,51 +522,55 @@ class InstanceMixin(object):
|
||||||
class GetResourceMixin(object):
|
class GetResourceMixin(object):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
resource = self.get_resource()
|
||||||
try:
|
try:
|
||||||
self.resource.retrieve(request, *args, **kwargs)
|
resource.retrieve(*args, **kwargs)
|
||||||
except self.resource.DoesNotExist:
|
except resource.DoesNotExist:
|
||||||
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
||||||
return self.resource.instance
|
return resource.instance
|
||||||
|
|
||||||
|
|
||||||
class PostResourceMixin(object):
|
class PostResourceMixin(object):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.resource.create(request, *args, **kwargs)
|
resource = self.get_resource()
|
||||||
self.resource.update(self.CONTENT, request, *args, **kwargs)
|
resource.create(*args, **kwargs)
|
||||||
headers = {'Location': self.resource.get_url()}
|
resource.update(self.CONTENT, *args, **kwargs)
|
||||||
return Response(status.HTTP_201_CREATED, self.resource.instance, headers)
|
headers = {'Location': resource.get_url()}
|
||||||
|
return Response(status.HTTP_201_CREATED, resource.instance, headers)
|
||||||
|
|
||||||
|
|
||||||
class PutResourceMixin(object):
|
class PutResourceMixin(object):
|
||||||
|
|
||||||
def put(self, request, *args, **kwargs):
|
def put(self, request, *args, **kwargs):
|
||||||
headers = {}
|
resource = self.get_resource()
|
||||||
try:
|
try:
|
||||||
self.resource.retrieve(request, *args, **kwargs)
|
resource.retrieve(*args, **kwargs)
|
||||||
status_code = status.HTTP_204_NO_CONTENT
|
status_code = status.HTTP_204_NO_CONTENT
|
||||||
except self.resource.DoesNotExist:
|
except resource.DoesNotExist:
|
||||||
self.resource.create(request, *args, **kwargs)
|
resource.create(*args, **kwargs)
|
||||||
status_code = status.HTTP_201_CREATED
|
status_code = status.HTTP_201_CREATED
|
||||||
self.resource.update(self.CONTENT, request, *args, **kwargs)
|
resource.update(self.CONTENT, *args, **kwargs)
|
||||||
return Response(status_code, self.resource.instance, {})
|
return Response(status_code, resource.instance, {})
|
||||||
|
|
||||||
|
|
||||||
class DeleteResourceMixin(object):
|
class DeleteResourceMixin(object):
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
|
resource = self.get_resource()
|
||||||
try:
|
try:
|
||||||
self.resource.retrieve(request, *args, **kwargs)
|
resource.retrieve(*args, **kwargs)
|
||||||
except self.resource.DoesNotExist:
|
except resource.DoesNotExist:
|
||||||
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
|
||||||
self.resource.delete(request, *args, **kwargs)
|
resource.delete(*args, **kwargs)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class ListResourceMixin(object):
|
class ListResourceMixin(object):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.resource.list(request, *args, **kwargs)
|
resource = self.get_resource()
|
||||||
|
return resource.list(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
########## Pagination Mixins ##########
|
########## Pagination Mixins ##########
|
||||||
|
|
|
@ -216,4 +216,4 @@ class PerResourceThrottling(BaseThrottle):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_cache_key(self):
|
def get_cache_key(self):
|
||||||
return 'throttle_resource_%s' % self.view.resource.__class__.__name__
|
return 'throttle_resource_%s' % self.view.get_resource_class().__name__
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.urlresolvers import reverse, get_urlconf, get_resolver, NoReverseMatch
|
from django.core.urlresolvers import reverse, get_urlconf, get_resolver, NoReverseMatch
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from djangorestframework.response import ErrorResponse
|
from djangorestframework.response import ErrorResponse
|
||||||
from djangorestframework.serializer import Serializer, _SkipField
|
from djangorestframework.serializer import Serializer, _SkipField
|
||||||
|
|
||||||
|
|
||||||
def bound_resource_required(meth):
|
class BaseResource(object):
|
||||||
def _decorated(self, *args, **kwargs):
|
|
||||||
if not self.is_bound():
|
|
||||||
raise Exception("resource needs to be bound") #TODO: what exception?
|
|
||||||
return meth(self, *args, **kwargs)
|
|
||||||
return _decorated
|
|
||||||
|
|
||||||
|
|
||||||
class BaseResource(Serializer):
|
|
||||||
"""
|
"""
|
||||||
Base class for all Resource classes, which simply defines the interface
|
Base class for all Resource classes, which simply defines the interface
|
||||||
they provide.
|
they provide.
|
||||||
|
@ -26,7 +19,8 @@ class BaseResource(Serializer):
|
||||||
# TODO: Inheritance, like for models
|
# TODO: Inheritance, like for models
|
||||||
class DoesNotExist(Exception): pass
|
class DoesNotExist(Exception): pass
|
||||||
|
|
||||||
def __init__(self, instance=None, view=None, depth=None, stack=[], **kwargs):
|
# !!! `view` should be first kwarg to avoid backward incompatibilities. (lol)
|
||||||
|
def __init__(self, view=None, instance=None, depth=None, stack=[], **kwargs):
|
||||||
super(BaseResource, self).__init__(depth, stack, **kwargs)
|
super(BaseResource, self).__init__(depth, stack, **kwargs)
|
||||||
self.view = view
|
self.view = view
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
|
@ -37,23 +31,20 @@ class BaseResource(Serializer):
|
||||||
Typically raises a :exc:`response.ErrorResponse` with status code 400
|
Typically raises a :exc:`response.ErrorResponse` with status code 400
|
||||||
(Bad Request) on failure.
|
(Bad Request) on failure.
|
||||||
"""
|
"""
|
||||||
return data
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def retrieve(self, *args, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@bound_resource_required
|
def create(self, *args, **kwargs):
|
||||||
def update(self, data, request, *args, **kwargs):
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@bound_resource_required
|
def update(self, data, *args, **kwargs):
|
||||||
def delete(self, request, *args, **kwargs):
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@bound_resource_required
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -61,7 +52,7 @@ class BaseResource(Serializer):
|
||||||
return not self.instance is None
|
return not self.instance is None
|
||||||
|
|
||||||
|
|
||||||
class Resource(BaseResource):
|
class Resource(Serializer, BaseResource):
|
||||||
"""
|
"""
|
||||||
A Resource determines how a python object maps to some serializable data.
|
A Resource determines how a python object maps to some serializable data.
|
||||||
Objects that a resource can act on include plain Python object instances,
|
Objects that a resource can act on include plain Python object instances,
|
||||||
|
@ -83,6 +74,9 @@ class Resource(BaseResource):
|
||||||
# you should explicitly set the fields attribute on your class.
|
# you should explicitly set the fields attribute on your class.
|
||||||
fields = None
|
fields = None
|
||||||
|
|
||||||
|
def deserialize(self, data, files=None):
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class FormResource(Resource):
|
class FormResource(Resource):
|
||||||
"""
|
"""
|
||||||
|
@ -148,6 +142,7 @@ class FormResource(Resource):
|
||||||
if bound_form is None:
|
if bound_form is None:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
if self.view is not None:
|
||||||
self.view.bound_form_instance = bound_form
|
self.view.bound_form_instance = bound_form
|
||||||
|
|
||||||
data = data and data or {}
|
data = data and data or {}
|
||||||
|
@ -314,17 +309,17 @@ class ModelResource(FormResource):
|
||||||
is not set.
|
is not set.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, instance=None, view=None, depth=None, stack=[], **kwargs):
|
def __init__(self, view=None, instance=None, depth=None, stack=[], **kwargs):
|
||||||
"""
|
"""
|
||||||
Allow :attr:`form` and :attr:`model` attributes set on the
|
Allow :attr:`form` and :attr:`model` attributes set on the
|
||||||
:class:`View` to override the :attr:`form` and :attr:`model`
|
:class:`View` to override the :attr:`form` and :attr:`model`
|
||||||
attributes set on the :class:`Resource`.
|
attributes set on the :class:`Resource`.
|
||||||
"""
|
"""
|
||||||
super(ModelResource, self).__init__(instance=instance, view=view, depth=depth, stack=stack, **kwargs)
|
super(ModelResource, self).__init__(view=None, instance=instance, depth=depth, stack=stack, **kwargs)
|
||||||
|
|
||||||
self.model = getattr(view, 'model', None) or self.model
|
self.model = getattr(view, 'model', None) or self.model
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return a model instance or None.
|
Return a model instance or None.
|
||||||
"""
|
"""
|
||||||
|
@ -339,7 +334,7 @@ class ModelResource(FormResource):
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, *args, **kwargs):
|
||||||
model = self.get_model()
|
model = self.get_model()
|
||||||
kwargs = self._clean_url_kwargs(kwargs)
|
kwargs = self._clean_url_kwargs(kwargs)
|
||||||
|
|
||||||
|
@ -347,8 +342,12 @@ class ModelResource(FormResource):
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
@bound_resource_required
|
def update(self, data, *args, **kwargs):
|
||||||
def update(self, data, request, *args, **kwargs):
|
# The resource needs to be bound to an
|
||||||
|
# instance, or updating is not possible
|
||||||
|
if not self.is_bound():
|
||||||
|
raise Exception("resource needs to be bound") #TODO: what exception?
|
||||||
|
|
||||||
model = self.get_model()
|
model = self.get_model()
|
||||||
kwargs = self._clean_url_kwargs(kwargs)
|
kwargs = self._clean_url_kwargs(kwargs)
|
||||||
data = dict(data, **kwargs)
|
data = dict(data, **kwargs)
|
||||||
|
@ -382,12 +381,16 @@ class ModelResource(FormResource):
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
@bound_resource_required
|
def delete(self, *args, **kwargs):
|
||||||
def delete(self, request, *args, **kwargs):
|
# The resource needs to be bound to an
|
||||||
|
# instance, or deleting is not possible
|
||||||
|
if not self.is_bound():
|
||||||
|
raise Exception("resource needs to be bound") #TODO: what exception?
|
||||||
|
|
||||||
self.instance.delete()
|
self.instance.delete()
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, *args, **kwargs):
|
||||||
# TODO: QuerysetResource instead !?
|
# TODO: QuerysetResource instead !?
|
||||||
kwargs = self._clean_url_kwargs(kwargs)
|
kwargs = self._clean_url_kwargs(kwargs)
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
|
@ -397,7 +400,6 @@ class ModelResource(FormResource):
|
||||||
queryset = queryset.order_by(ordering)
|
queryset = queryset.order_by(ordering)
|
||||||
return queryset.filter(**kwargs)
|
return queryset.filter(**kwargs)
|
||||||
|
|
||||||
@bound_resource_required
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
"""
|
"""
|
||||||
Attempts to reverse resolve the url of the given model *instance* for
|
Attempts to reverse resolve the url of the given model *instance* for
|
||||||
|
@ -409,6 +411,10 @@ class ModelResource(FormResource):
|
||||||
This method can be overridden if you need to set the resource url
|
This method can be overridden if you need to set the resource url
|
||||||
reversing explicitly.
|
reversing explicitly.
|
||||||
"""
|
"""
|
||||||
|
# The resource needs to be bound to an
|
||||||
|
# instance, or getting url is not possible
|
||||||
|
if not self.is_bound():
|
||||||
|
raise Exception("resource needs to be bound") #TODO: what exception?
|
||||||
|
|
||||||
if not hasattr(self, 'view_callable'):
|
if not hasattr(self, 'view_callable'):
|
||||||
raise _SkipField
|
raise _SkipField
|
||||||
|
@ -527,18 +533,36 @@ class ModelResource(FormResource):
|
||||||
|
|
||||||
def get_model(self):
|
def get_model(self):
|
||||||
"""
|
"""
|
||||||
Return the model class for this view.
|
Return the model class for this resource.
|
||||||
"""
|
"""
|
||||||
return getattr(self, 'model', getattr(self.view, 'model', None))
|
model = getattr(self, 'model', None)
|
||||||
|
if model is None:
|
||||||
|
model = getattr(self.view, 'model', None)
|
||||||
|
if model is None:
|
||||||
|
raise ImproperlyConfigured(u"%(cls)s is missing a model. Define "
|
||||||
|
u"%(cls)s.model." % {
|
||||||
|
'cls': self.__class__
|
||||||
|
})
|
||||||
|
return model
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Return the queryset that should be used when retrieving or listing
|
Return the queryset that should be used when retrieving or listing
|
||||||
instances.
|
instances.
|
||||||
"""
|
"""
|
||||||
return getattr(self, 'queryset',
|
queryset = getattr(self, 'queryset', None)
|
||||||
getattr(self.view, 'queryset',
|
if queryset is None:
|
||||||
self.get_model().objects.all()))
|
queryset = getattr(self.view, 'queryset', None)
|
||||||
|
if queryset is None:
|
||||||
|
try:
|
||||||
|
model = self.get_model()
|
||||||
|
except ImproperlyConfigured:
|
||||||
|
raise ImproperlyConfigured(u"%(cls)s is missing a queryset. Define "
|
||||||
|
u"%(cls)s.model or %(cls)s.queryset." % {
|
||||||
|
'cls': self.__class__
|
||||||
|
})
|
||||||
|
queryset = model._default_manager.all()
|
||||||
|
return queryset._clone()
|
||||||
|
|
||||||
def get_ordering(self):
|
def get_ordering(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -157,7 +157,7 @@ class RendererIntegrationTests(TestCase):
|
||||||
|
|
||||||
_flat_repr = '{"foo": ["bar", "baz"]}'
|
_flat_repr = '{"foo": ["bar", "baz"]}'
|
||||||
|
|
||||||
_indented_repr = '{\n "foo": [\n "bar", \n "baz"\n ]\n}'
|
_indented_repr = '{\n "foo": [\n "bar",\n "baz"\n ]\n}'
|
||||||
|
|
||||||
|
|
||||||
class JSONRendererTests(TestCase):
|
class JSONRendererTests(TestCase):
|
||||||
|
|
|
@ -201,10 +201,11 @@ class ListModelView(ListResourceMixin, ModelView):
|
||||||
A view which provides default operations for list, against a model in the
|
A view which provides default operations for list, against a model in the
|
||||||
database.
|
database.
|
||||||
"""
|
"""
|
||||||
|
queryset = None
|
||||||
_suffix = 'List'
|
_suffix = 'List'
|
||||||
|
|
||||||
|
|
||||||
class ListOrCreateModelView(PostResourceMixin, ListResourceMixin, ModelView):
|
class ListOrCreateModelView(PostResourceMixin, ListModelView, ModelView):
|
||||||
"""
|
"""
|
||||||
A view which provides default operations for list and create, against a
|
A view which provides default operations for list and create, against a
|
||||||
model in the database.
|
model in the database.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user