views:

274

answers:

2

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?

+1  A: 

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.

Daniel Roseman
Oh... so how do I get around that?
Mark
+3  A: 

This decorator already exists as django.contrib.admin.views.decorators.staff_member_required.

1.1.1 release source: http://code.djangoproject.com/browser/django/tags/releases/1.1.1/django/contrib/admin/views/decorators.py
Trunk: http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/views/decorators.py

Mark Lavin
Oh...cool. I still want to know how I'd code it though ;)
Mark
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.
Mark
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.
Peter Rowell
@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.
Mark
@Mark: Luke is ... Luke Skywalker! Obi Wan Kenobi (http://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. http://www.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.
Peter Rowell
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.
Mark