Search code examples
pythondjangodjango-modelsdjango-viewsdjango-class-based-views

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?


Solution

  • 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'),