Sarah
Sarah

Reputation: 516

How to test user ownership of object using Django decorators

I'm working on a Django project and trying to figure out how I can test for user ownership and allow editing or redirect based on the result.

I have a model Scene. Scene is linked to User to track which user created a particular Scene:

class Scene(models.Model):
    user = models.ForeignKey(User)
    [rest of Scene model]

I have a URL pattern to edit a particular Scene object like this:

url(r'^scenes/(?P<pk>[0-9]+)/edit/', SceneUpdateView.as_view(), name='scene-edit'),

I have a logged in user via django-allauth. I want only Scene owners to be able to edit Scenes.

I'm trying to figure out how to use a decorator to test if scene.user.id == self.request.user.id for the particular scene called by the URL.

Do I need to send URL information into permission_required or user_passes_test decorators (is this possible)?

How can I make this happen?

Upvotes: 6

Views: 2710

Answers (2)

Gon&#231;alo Peres
Gon&#231;alo Peres

Reputation: 13582

In Function Based Views it's common to see decorators. Yet, in Class Based Views (CBV) it's more common to use Mixins or QuerySet.

Adapted from this answer, one can create the following custom Mixin that overrides the dispatch method

class UserOwnerMixin(object):
    def dispatch(self, request, *args, **kwargs):
        if self.object.user != self.request.user:
            return HttpResponseForbidden()
        return super(UserOwnerMixin, self).dispatch(request, *args, **kwargs)

This is a generalized way across multiple model class, as long as one is using user = models.ForeignKey(User).

Then use it in the CBV in a similar fashion to

class MyCustomView(UserOwnerMixin, View):

Upvotes: 1

Yahya Yahyaoui
Yahya Yahyaoui

Reputation: 2953

You can use a custom decorator for your specefic need. Note: I'm using function based view, you will have to modify the code to class based view if you want:

import json

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.models import User
from yourmodels.models import Scene

#Custom decorator
def must_be_yours(func):
    def check_and_call(request, *args, **kwargs):
        #user = request.user
        #print user.id
        pk = kwargs["pk"]
        scene = Scene.objects.get(pk=pk)
        if not (scene.user.id == request.user.id): 
            return HttpResponse("It is not yours ! You are not permitted !",
                        content_type="application/json", status=403)
        return func(request, *args, **kwargs)
    return check_and_call

#View Function
@must_be_yours
@csrf_protect
def update_scene(request, pk=None):
    print pk
    if request.method == 'PUT':
        #modify merely
        pass

Urls:

url(r'^scenes/(?P<pk>[0-9]+)/edit/', 'update_scene'),

Upvotes: 7

Related Questions