diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index f4a9c998a..d5eeac818 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -527,6 +527,38 @@ class ModelMixin(object): """ return self.get_queryset().get(**kwargs) + def get_owner(self): + """ + Returns the model instance's owner, if any. + + The owner is retrieved by calling the .get_owner() function on the model instance, if implemented. + """ + try: + return self.model_instance.get_owner() + except: pass + + @property + def model_instance(self): + """ + Returns the model instance for read/update/delete, or None if does not exist. + """ + + if hasattr(self, '_model_instance'): return self._model_instance + + model = self.resource.model + request = self.request + args = self.args + kwargs = self.kwargs + + query_kwargs = self.get_query_kwargs(request, *args, **kwargs) + + try: + self._model_instance = self.get_instance(**query_kwargs) + except model.DoesNotExist: + pass + + return self._model_instance + def get_queryset(self): """ Return the queryset for this view. @@ -546,12 +578,8 @@ class ReadModelMixin(ModelMixin): Behavior to read a `model` instance on GET requests """ def get(self, request, *args, **kwargs): - model = self.resource.model - query_kwargs = self.get_query_kwargs(request, *args, **kwargs) - try: - self.model_instance = self.get_instance(**query_kwargs) - except model.DoesNotExist: + if self.model_instance == None: raise ErrorResponse(status.HTTP_404_NOT_FOUND) return self.model_instance @@ -602,18 +630,17 @@ class UpdateModelMixin(ModelMixin): Behavior to update a `model` instance on PUT requests """ def put(self, request, *args, **kwargs): + model = self.resource.model - query_kwargs = self.get_query_kwargs(request, *args, **kwargs) # TODO: update on the url of a non-existing resource url doesn't work # correctly at the moment - will end up with a new url - try: - self.model_instance = self.get_instance(**query_kwargs) - + if self.model_instance == None: + self.model_instance = model(**self.get_instance_data(model, self.CONTENT, *args, **kwargs)) + else: for (key, val) in self.CONTENT.items(): setattr(self.model_instance, key, val) - except model.DoesNotExist: - self.model_instance = model(**self.get_instance_data(model, self.CONTENT, *args, **kwargs)) + self.model_instance.save() return self.model_instance @@ -623,15 +650,9 @@ class DeleteModelMixin(ModelMixin): Behavior to delete a `model` instance on DELETE requests """ def delete(self, request, *args, **kwargs): - model = self.resource.model - query_kwargs = self.get_query_kwargs(request, *args, **kwargs) - try: - instance = self.get_instance(**query_kwargs) - except model.DoesNotExist: - raise ErrorResponse(status.HTTP_404_NOT_FOUND, None, {}) - - instance.delete() + if self.model_instance == None: raise ErrorResponse(status.HTTP_404_NOT_FOUND, None, {}) + self.model_instance.delete() return diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py index dfe55ce94..8d02c4e5a 100644 --- a/djangorestframework/permissions.py +++ b/djangorestframework/permissions.py @@ -13,6 +13,7 @@ __all__ = ( 'BasePermission', 'FullAnonAccess', 'IsAuthenticated', + 'IsResourceOwnerOrIsAnonReadOnly', 'IsAdminUser', 'IsUserOrIsAnonReadOnly', 'PerUserThrottling', @@ -77,6 +78,25 @@ class IsAdminUser(BasePermission): raise _403_FORBIDDEN_RESPONSE +class IsResourceOwnerOrIsAnonReadOnly(BasePermission): + """ + The request is authenticated as the owner of the resource, or is a read-only request. + """ + + def check_permission(self, user): + + if self.view.method in('GET', 'HEAD',): + return + + if not user.is_authenticated(): + raise _403_FORBIDDEN_RESPONSE + + if self.view.get_owner() == user: + return + + raise _403_FORBIDDEN_RESPONSE + + class IsUserOrIsAnonReadOnly(BasePermission): """ The request is authenticated as a user, or is a read-only request. diff --git a/djangorestframework/resources.py b/djangorestframework/resources.py index cc338cc05..7d0d1d5ff 100644 --- a/djangorestframework/resources.py +++ b/djangorestframework/resources.py @@ -32,6 +32,12 @@ class BaseResource(Serializer): """ return self.serialize(obj) + def get_owner(self): + """ + Returns a Django User instance as the owner of the resource, if any. + """ + return None + class Resource(BaseResource): """