diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index 99bc6303d..71d90e70e 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -471,12 +471,21 @@ class ResourceMixin(object): """ return self.deserialize(self.request.GET) - @property - def resource(self): - if not hasattr(self, '_resource'): - self._resource = self.resource_class(view=self) - return self._resource + def get_resource_class(self): + if self.resource_class: + return self.resource_class + elif getattr(self, 'model', None): + 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. """ 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) if resource_class: # We do a little dance when we store the view callable... @@ -512,51 +522,55 @@ class InstanceMixin(object): class GetResourceMixin(object): def get(self, request, *args, **kwargs): + resource = self.get_resource() try: - self.resource.retrieve(request, *args, **kwargs) - except self.resource.DoesNotExist: + resource.retrieve(*args, **kwargs) + except resource.DoesNotExist: raise ErrorResponse(status.HTTP_404_NOT_FOUND) - return self.resource.instance + return resource.instance class PostResourceMixin(object): def post(self, request, *args, **kwargs): - self.resource.create(request, *args, **kwargs) - self.resource.update(self.CONTENT, request, *args, **kwargs) - headers = {'Location': self.resource.get_url()} - return Response(status.HTTP_201_CREATED, self.resource.instance, headers) + resource = self.get_resource() + resource.create(*args, **kwargs) + resource.update(self.CONTENT, *args, **kwargs) + headers = {'Location': resource.get_url()} + return Response(status.HTTP_201_CREATED, resource.instance, headers) class PutResourceMixin(object): def put(self, request, *args, **kwargs): - headers = {} + resource = self.get_resource() try: - self.resource.retrieve(request, *args, **kwargs) + resource.retrieve(*args, **kwargs) status_code = status.HTTP_204_NO_CONTENT - except self.resource.DoesNotExist: - self.resource.create(request, *args, **kwargs) + except resource.DoesNotExist: + resource.create(*args, **kwargs) status_code = status.HTTP_201_CREATED - self.resource.update(self.CONTENT, request, *args, **kwargs) - return Response(status_code, self.resource.instance, {}) + resource.update(self.CONTENT, *args, **kwargs) + return Response(status_code, resource.instance, {}) class DeleteResourceMixin(object): def delete(self, request, *args, **kwargs): + resource = self.get_resource() try: - self.resource.retrieve(request, *args, **kwargs) - except self.resource.DoesNotExist: + resource.retrieve(*args, **kwargs) + except resource.DoesNotExist: raise ErrorResponse(status.HTTP_404_NOT_FOUND) - self.resource.delete(request, *args, **kwargs) + resource.delete(*args, **kwargs) return class ListResourceMixin(object): def get(self, request, *args, **kwargs): - return self.resource.list(request, *args, **kwargs) + resource = self.get_resource() + return resource.list(*args, **kwargs) ########## Pagination Mixins ########## diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index 945023cec..953f18613 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -216,4 +216,4 @@ class PerResourceThrottling(BaseThrottle): """ def get_cache_key(self): - return 'throttle_resource_%s' % self.view.resource.__class__.__name__ + return 'throttle_resource_%s' % self.view.get_resource_class().__name__ diff --git a/djangorestframework/resources.py b/djangorestframework/resources.py index e5fa7b6c9..fde91e305 100644 --- a/djangorestframework/resources.py +++ b/djangorestframework/resources.py @@ -6,14 +6,6 @@ from djangorestframework.response import ErrorResponse from djangorestframework.serializer import Serializer, _SkipField -def bound_resource_required(meth): - 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 @@ -26,7 +18,8 @@ class BaseResource(Serializer): # TODO: Inheritance, like for models 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) self.view = view self.instance = instance @@ -39,21 +32,18 @@ class BaseResource(Serializer): """ return data - def retrieve(self, request, *args, **kwargs): + def retrieve(self, *args, **kwargs): raise NotImplementedError() - def create(self, request, *args, **kwargs): + def create(self, *args, **kwargs): raise NotImplementedError() - @bound_resource_required - def update(self, data, request, *args, **kwargs): + def update(self, data, *args, **kwargs): raise NotImplementedError() - @bound_resource_required - def delete(self, request, *args, **kwargs): + def delete(self, *args, **kwargs): raise NotImplementedError() - @bound_resource_required def get_url(self): raise NotImplementedError() @@ -148,7 +138,8 @@ class FormResource(Resource): if bound_form is None: return data - self.view.bound_form_instance = bound_form + if self.view is not None: + self.view.bound_form_instance = bound_form data = data and data or {} files = files and files or {} @@ -314,17 +305,17 @@ class ModelResource(FormResource): 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 :class:`View` to override the :attr:`form` and :attr:`model` 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 - def retrieve(self, request, *args, **kwargs): + def retrieve(self, *args, **kwargs): """ Return a model instance or None. """ @@ -339,7 +330,7 @@ class ModelResource(FormResource): self.instance = instance return self.instance - def create(self, request, *args, **kwargs): + def create(self, *args, **kwargs): model = self.get_model() kwargs = self._clean_url_kwargs(kwargs) @@ -347,8 +338,12 @@ class ModelResource(FormResource): self.instance.save() return self.instance - @bound_resource_required - def update(self, data, request, *args, **kwargs): + def update(self, data, *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() kwargs = self._clean_url_kwargs(kwargs) data = dict(data, **kwargs) @@ -382,12 +377,16 @@ class ModelResource(FormResource): self.instance.save() return self.instance - @bound_resource_required - def delete(self, request, *args, **kwargs): + def delete(self, *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() return self.instance - def list(self, request, *args, **kwargs): + def list(self, *args, **kwargs): # TODO: QuerysetResource instead !? kwargs = self._clean_url_kwargs(kwargs) queryset = self.get_queryset() @@ -397,7 +396,6 @@ class ModelResource(FormResource): queryset = queryset.order_by(ordering) return queryset.filter(**kwargs) - @bound_resource_required def get_url(self): """ Attempts to reverse resolve the url of the given model *instance* for @@ -409,6 +407,10 @@ class ModelResource(FormResource): This method can be overridden if you need to set the resource url 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'): raise _SkipField diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py index a8b784161..c3dfb98b6 100644 --- a/djangorestframework/tests/renderers.py +++ b/djangorestframework/tests/renderers.py @@ -157,7 +157,7 @@ class RendererIntegrationTests(TestCase): _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):