How to limit access to the UpdateView of an object to the creator of that object
Asked Answered
O

3

7

Django and programming noob here. I've made an application I'd like to deploy, but I need to figure out how to limit access to the UpdateView to the creator of that object, and I'm stumped.

Currently a user can use the CreateView .../universities/create/ to create a university object, but then any user can use .../universities/update/ to edit that object. I want to configure this so only the user who is the creator (any user with the ManytoMany attribute 'administrator') of that university has access to the UpdateView for their university object.

Any advice would be appreciated. I've spent a few days on this and I haven't made much traction...thanks for reading.

models.py

class University(models.Model):
    name = models.CharField(max_length=100)
    about = models.TextField()
    administrators = models.ManyToManyField(User)
    profile_picture = models.FileField(upload_to=get_upload_file_name, blank=True)

    def __unicode__(self):
        return unicode(self.name)

    def get_absolute_url(self):
        return reverse('university_detail', kwargs={'pk': str(self.id)})

views.py

class UniversityCreateView(CreateView):
    model = University
    form_class = UniversityForm
    template_name = 'university_create.html'

    def form_valid(self, form):
        f = form.save(commit=False)
        f.save()
        return super(UniversityCreateView, self).form_valid(form)

class UniversityUpdateView(UpdateView):
    model = University
    form_class = UniversityForm
    template_name='university_form.html'
Officiate answered 28/8, 2013 at 0:51 Comment(5)
possible duplicate of Django: UpdateView restrict per userDeonnadeonne
I see this has been marked as a duplicate, but I don't see how that post helps me. I am not trying to display a page where the user can receive a list of the Universities that they are the administrator for. I am trying to prevent anyone who is not the administrator for a university from accessing the UpdateView for that university.Officiate
If I am misunderstanding and that 'possible duplicate' link does in fact provide me with the solution I need, I'd appreciate some explanation how that works.Officiate
This hasn't been marked as a duplicate, I made that suggestion because the person who asked that other question seemed to want the same thing (restrict UpdateView to objects created by the logged in user). According to the documentation, the get_queryset method in the UpdateView, "returns the queryset that will be used to retrieve the object that this view will display". So if you restrict the user of the queryset, objects created by other users should not show up.Deonnadeonne
Ah ok thank you for the explanation, get_queryset at least looks like what I will need soon for another feature of my UpdateView. I will keep trying to see if it resolves this current permissions issue.Officiate
L
4

You can use UserPassesTestMixin as the documentation says:

limit access based on certain permissions or some other test

just implement test_func(self) that returns True if the user should enter the view.

You might write a code like this:

class UniversityUpdateView(UserPassesTestMixin,UpdateView):
    def test_func(self):
        return self.request.user.administrators_set.filter(pk=self.get_object().pk).exists()
    model = University
    form_class = UniversityForm
    template_name='university_form.html'
Leucopoiesis answered 26/1, 2018 at 18:51 Comment(0)
T
2

youll have to include permission decorators on your views , further info is here https://docs.djangoproject.com/en/dev/topics/auth/ , & https://docs.djangoproject.com/en/dev/topics/auth/default/#topic-authorization

so if you want to limit your updateview to any user with the ManytoMany attribute 'administrator', youll have to do something like this:

views.py

from appname.users.decorators import requiresGroup
from django.contrib.auth.decorators import login_required



class UniversityUpdateView(UpdateView):
    model = University
    form_class = UniversityForm
    template_name='university_form.html'

    @method_decorator(requiresGroup("groupname" , login_url='/accounts/login/'))
    def dispatch(self, request, *args, **kwargs):
    return super(UniversityUpdateView, self).dispatch(request, *args, **kwargs)

also if you havent already youll have to include the following at the top of your models.py

from django.contrib.auth.modes import user

though Ill assume its there as youve defined your administrators with the user model

then go to the group seetings in the django admin ( should be a url like localhost/admin/auth/group , add your special adminstrator group name, then go to the admin user section (localhost/admin/auth/user), then make sure they have been put into the adminstrator group

then replace "groupname" in the @requiresGroup decorator with the actual name of the user group

the @requiresGroup decorator isnt a standard decorator, so it has to be written

make a folder path and file like appname/users.decorators.py then in decorators.py write

from functools import update_wrapper , wraps
from django.utils.decorators import available_attrs
from django.http import HttpResponse, HttpResponseRedirect


def requiresGroup(groupname):
    def decorator(view_function):
        def _wrapped_view(request,*args,**kwargs):
            if request.user.groups.filter(name=groupname).count()!=1:
                return HttpResponseRedirect("/")
            else:
                return view_function(request,*args,**kwargs)
        return wraps(view_function,assigned=available_attrs(view_function))(_wrapped_view)
    return decorator

hope this helped

edit: made a mistake, put the decorators above the class, they should be in a function inside the class, noticed my mistake almost immediately so hopefully I havent caused any trouble

Trudge answered 29/8, 2013 at 9:32 Comment(0)
P
0

You can override the get method of your class based view (in this case UniversityUpdateView). Then in the method check if user has rights to access the page and if not raise exception or redirect the user to another page. If the user has enough rights to access the page then just let the normal behavior go on.

class UniversityUpdateView(UpdateView):
    model = University
    form_class = UniversityForm
    template_name='university_form.html'

    def get(self, request, *args, **kwargs):
        if request.user.groups.filter(name=groupname).count()!=1:
                return HttpResponseRedirect("/")
        return super().get(request, *args, **kwargs)
Perretta answered 20/4, 2020 at 8:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.