How to test user ownership of object using Django decorators
Asked Answered
F

2

6

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?

Franz answered 3/7, 2015 at 2:26 Comment(1)
Possible duplicate of #11873060, and if this isn't a duplicate, this at least should give some hints on how to solve this.Beata
S
7

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'),
Sternutatory answered 3/7, 2015 at 3:54 Comment(1)
Is there any way of generalizing this across multiple model class??Onshore
H
1

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):
Habanera answered 31/8, 2022 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.