sybaritic
sybaritic

Reputation: 402

Object ownership validation in Django UpdateView

EDIT:

The better solution for me was just using a permissions system, especially since I needed other types of controlled access to objects. I now use Django-guardian to help with object level permissions like this.

Original:

I'm expanding a bit on the standard django book guide by letting users upload stories, as well as having author, publisher, etc. I'm attempting to only let authors (creators) of a story use the updateview, with other users being redirected away.

Modifying get_object in the UpdateStory view set it off, but the traceback goes through my StoryForm init for some reason. The error is 'HttpResponseRedirect' object has no attribute '_meta'

views.py

class UpdateStory(LoginRequiredMixin, UpdateView):
    model = Story
    template_name = 'stories/story_update.html'
    form_class = StoryForm

    def get_object(self, queryset=None):
        obj = super(UpdateStory, self).get_object()
        if not obj.author == self.request.user:
            return redirect(obj)
        return obj

forms.py

class StoryForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(StoryForm,self).__init__(*args, **kwargs)

I'm still new, so it might be obvious, but I've been looking for a couple hours and I'm stumped.

Upvotes: 6

Views: 4570

Answers (3)

Gonçalo Peres
Gonçalo Peres

Reputation: 13582

This specific issue is considered in Django anti-patterns.

We're encouraged to filter the QuerySet to only retrieve objects where the user is the author, as opposed to UserPassesTestMixin.

In OP's case it would actually be quite similar to what they have there

from django.contrib.auth.mixins import LoginRequiredMixin

class UpdateStory(LoginRequiredMixin, UpdateView):
    model = Story
    # …

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).filter(
            author=self.request.user
        )

Upvotes: 2

Berislav Lopac
Berislav Lopac

Reputation: 17243

The best approach would be to use another mixin, something like this:

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

Of course you can return another HttpResponse, but keep in mind what is the proper use here.

Upvotes: 9

Rag Sagar
Rag Sagar

Reputation: 2374

http://ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/UpdateView/

Go through the above link to understand how UpdateView works. get_object is supposed to return the model instance, It is not supposed to return HttpResponseRedirect object, that's why you are getting that error.

Try doing the check in dispatch method like the following.

def dispatch(self, request, *args, **kwargs):
    """ Making sure that only authors can update stories """
    obj = self.get_object()
    if obj.author != self.request.user:
        return redirect(obj)
    return super(UpdateStory, self).dispatch(request, *args, **kwargs)

PS: I guess it is not recommended to override dispatch. But as you have to do the check on both get and post methods, overriding dispatch will be easier.

Upvotes: 5

Related Questions