Django: Staff Decorator
Asked Answered
A

5

62

I'm trying to write a "staff only" decorator for Django, but I can't seem to get it to work:

def staff_only(error='Only staff may view this page.'):
    def _dec(view_func):
        def _view(request, *args, **kwargs):
            u = request.user
            if u.is_authenticated() and u.is_staff:
                return view_func(request, *args, **kwargs)
            messages.error(request, error)
            return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse('home')))
        _view.__name__ = view_func.__name__
        _view.__dict__ = view_func.__dict__
        _view.__doc__ = view_func.__doc__
        return _view
    return _dec

Trying to follow lead from here. I'm getting:

'WSGIRequest' object has no attribute '__name__'

But if I take those 3 lines out, I just get a useless "Internal Server Error". What am I doing wrong here?

Arta answered 22/4, 2010 at 19:28 Comment(0)
O
174

This decorator already exists as

from django.contrib.admin.views.decorators import staff_member_required

@staff_member_required

Trunk: http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/views/decorators.py

Otalgia answered 22/4, 2010 at 20:10 Comment(5)
Also, the problem with that is that it doesn't display an error message indicating what the problem is. My clients are quite likely to try and visit a staff page without a staff user account.Arta
Luke (or Mark), Use The Source! In django/contrib/admin/views/decorators.py (line 26 in the 1.1.1 release) you'll find this function, and on line 71 you'll see if user.is_active and user.is_staff: which gives the thumbs up/down decision. The only reason this particular decorator is a little more complicated than normal is because it can trigger a login sequence as a side effect. If you need it to do more (e.g. different error messages), you can always make a copy and then invoke your own decorator where appropriate.Cost
@Peter: Who's Luke? The source isn't very helpful in this case because it doesn't accept arguments to the decorator which I think is what's causing my problem.Arta
@Mark: Luke is ... Luke Skywalker! Obi Wan Kenobi (en.wikipedia.org/wiki/Obi-Wan_Kenobi) was the one who said, "Luke! User the Source!" in Star Wars IV (which was actually the first movie out). Decorators normally only take one argument, the function to be decorated. If you need to add more args, checkout a tutorial. (e.g. artima.com/weblogs/viewpost.jsp?thread=240845) If the current decorator doesn't do what you need, copy it and change it! This is the essence of Open Source.Cost
I thought it was "Use the Force"? Cute play on words ;) Isn't that what I've done tho? My code looks a lot like the example you pointed to... Oh well... guess I'll have to figure it out myself then.Arta
K
27

For Class Based Views, you can decorate the view class's dispatch method like this:

from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator


@method_decorator(staff_member_required, name='dispatch')
class ExampleTemplateView(TemplateView):
    ...
Kaete answered 12/10, 2017 at 14:56 Comment(0)
Q
11

This style of decorator function is used with a parameterised decorator - eg when you do:

@staffonly(my_arguments)
def function(request):
    blah

If you're not actually calling the outer function, ie you're using it like this:

@staffonly
def function(request):

You will get odd results, as the function object will be passed to the wrong one of the nested functions in the decorator.

Quash answered 22/4, 2010 at 19:59 Comment(2)
Oh... so how do I get around that?Arta
I'm starting to understand, so if I have multiple functions in my view, I have to add @staffonly in front of each one of them?Mallorymallow
F
0

I use Your Decorator and I face only one error:-

'bool' object is not callable

this error comes from here if u.is_authenticated() and u.is_staff: I change u.is_authenticated() to u.is_authenticated and it nicely works for me

Fossette answered 16/11, 2022 at 21:19 Comment(0)
E
0

This does not directly answer this question, but when I landed here, I needed to protect a view in a customized admin site. I needed to use the admin_view wrapper, see here. The code sample:

class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super().get_urls()
        my_urls = [path("my_view/", self.admin_site.admin_view(self.my_view))]
        return my_urls + urls

    def my_view(self, request):
        # ...
Edmund answered 5/7 at 18:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.