Reputation: 3076
In my legacy code, I've found two method of writing view decorators. 1st:
def require_session(f):
def wrap(request, *args, **kwargs):
if not 'username' in request.session:
messages.warning(request,_('You have to login first before you can access this page.'))
return redirect('/login')
return f(request, *args, **kwargs)
wrap.__doc__=f.__doc__
wrap.__name__=f.__name__
return wrap
and 2nd:
def require_role(role):
def decorator(func):
def inner_decorator(request,*args, **kwargs):
user_role = request.session.get('role','user')
if user_role != role:
if user_role == 'user':
return redirect('/dashboard')
else:
return redirect('/dashboard/list_users')
return func(request, *args, **kwargs)
return wraps(func)(inner_decorator)
return decorator
Which one is better and more "pythonic"? Or maybe should I write it in a totally different way? What is your way to write decorators?
Upvotes: 0
Views: 76
Reputation: 599610
These do different things. The first is a standard decorator, ie it wraps a function and returns the wrapped version, that when called does some stuff before calling the original. It is used in the standard way:
@require_session
def function_that_requires_session(request):
...
The second is a parameterized decorator. That is, the decorator itself needs a parameter - in this case, which exact role is required - and so the definition needs to accept a parameter and then return an actual decorator, which does the same as the standard version above. That's why you have an extra level of nested functions. So, this would be used like this:
@require_role(role_that_is_required)
def function_that_requires_role(request):
...
As you can see, the decorator takes a parameter.
The other difference between the two is that the first is using a manual way of transferring the name/docstring to the decorated function, whereas the second is using functools.wraps()
. You should do the latter (although as lvc points out, you can use it as a decorator, which is even clearer).
Upvotes: 3
Reputation: 35069
The usual way to handle copying the docstring and other metadata is to use functools.wraps
, like in your second example, except that you can use its return value as a decorator:
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# do stuff
return wrapper
Upvotes: 1