Understanding decorators in Django

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Understanding decorators in Django



I'm currently trying to build my own decorator for function based views.



I understood the basic concept behind decorators but I'm still struggling to fully understand whats happening "behind the scenes".



I've this example decorator. Could someone explain me what happens here or how it works? And why do we need so many functions here?


def active_user_required():

def decorator(function):
def wrapper(request, *args, **kw):
if request.user.is_active:
return function(request, *args, **kw)
else:
raise PermissionDenied()
return wrapper
return decorator





This is unnecessarily complicated. The main reason to have three levels of function is if the decorator itself takes parameters, but that's not the case here. The middle layer could be dropped.
– Daniel Roseman
Aug 8 at 18:13





Note though that decorators are a general Python thing; there is nothing specific to Django.
– Daniel Roseman
Aug 8 at 18:15





@DanielRoseman Thanks, I didn't know that the outer function is unnecessary - so can I omit the outer function and rename decorator to active_user_required?
– sascha
Aug 8 at 18:18





Yes. That would change the way you use it; you would then do just @active_user_required rather than @active_user_required().
– Daniel Roseman
Aug 8 at 18:19



@active_user_required


@active_user_required()




1 Answer
1



The key to understanding decorators in Python boils down to this: functions in Python are first-class objects. That means they can be passed as arguments and returned as values (among other things).



In the simplest form, a decorator takes a function as an argument and returns a new function that extends its functionality. For example, here is a decorator plusone that adds 1 to the original function's return value:


plusone


>>> def plusone(f):
... def wraps(*a):
... return f(*a) + 1
... return wraps
...
>>>
>>> @plusone
... def add(x, y):
... return x + y
...
>>>
>>> add(1, 2)
4



Reiterating, the important point about plusone isn't that it "adds 1", it's that it creates a new function (called wraps in this case) and returns that. On the last line, when add(1, 2) is called, it's actually the function wraps that is getting called (which, recall, calls your original add function). And you can prove it:


plusone


wraps


add(1, 2)


wraps


add


>>> add
<function wraps at 0x107b88578>



(Without the decorator, that would say <function add at 0x...>)


<function add at 0x...>



This can be extended to more complicated cases, such as decorators that take arguments of their own, but I don't think those details are to the root of your question. The key takeaway once more is that a decorator takes a function as an argument and then returns a function (and that returned function almost always extends the passed function in some way).



Hope that helps.





Thank you for the great answer, that helps me a lot. This means that the wraps function and the add function must have the same parameters?
– sascha
Aug 8 at 18:55






No problem, glad to help! Nothing in Python requires or checks that the functions take the same parameters, but in practice that's right, add and wraps should match. (The parameters don't need to have the same names though, and it is ok to collapse them with *a and **kw like I did in this example.) There are some cases where it makes sense for a decorator to change the function signature and it's definitely possible, but it's not a common use by any means.
– chris
Aug 9 at 0:24


add


wraps


*a


**kw






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard