Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple decorators for a view in Django: Execution order

I am trying to decorate a Django view by two decorators, one for checking login, and one for checking is_active.

The first one is the built-in @login_required, and the second one is the following:

def active_required(function):     dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')     return dec(function) 

Now, the decorators in Python work inside out, however the following does not work:

@active_required @login_required def foo(request):     ... 

I want to first check if the user is logged in, and redirect to login page if not, and if he or she is logged in, I want to check whether he or she is active, and if not, perform redirect to '/notallowed'.

What happens is that if the login_required fails, the user is not redirected to the login page, but @active_required is executed, and since the user is null in that case, @active_required decorator fails and the user is redirected to /notallowed.

Changing the order seems to work,

@login_required @active_required def foo(request):     ... 

but I suspect there is something wrong with this approach too.

What is the proper way to combine two decorators, and why does the execution order differ from simple Python decorators?

like image 795
ustun Avatar asked Jan 03 '12 16:01

ustun


People also ask

What order are decorators executed in Python?

During the definition, decorators are evaluated from bottom to top meanwhile during the execution (which is the most important part in general) they are evaluated from top to bottom.

Can we chain multiple decorators in Python?

Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together.

How many decorators are in Python?

In fact, there are two types of decorators in Python — class decorators and function decorators — but I will focus on function decorators here.

What is the use of decorators in Django?

Decorators are a way to restrict access to views based on the request method or control caching behaviour. This is particularly useful when you want to separate logged-in users from unauthenticated users or create an admin page that only privileged users can access.


2 Answers

Now, the decorators in Python work from the inside out

Well I guess that depends on your definition of inside out. In your case, you want @login_required to execute first, and so it should be the "outermost" (top) decorator.

As you noted, your last example works, and is indeed the correct way to do this.

edit

The confusion might be how these particular decorators work.

@login_required(@original_view) returns a new view, which first checks if you are logged in, and then calls original_view

so

     @login_required(         @active_required(             @my_view         )     ) 
first checks if you are logged in, then     first(second) checks if you are active, then         runs my_view 
like image 74
second Avatar answered Oct 04 '22 05:10

second


Decorators are applied in the order they appear in the source. Thus, your second example:

@login_required @active_required def foo(request):     ... 

is equivalent to the following:

def foo(request):     ... foo = login_required(active_required(foo)) 

Thus, if the code of one decorator depends on something set by (or ensured by) another, you have to put the dependent decorator "inside" the depdended-on decorator.

However, as Chris Pratt notes, you should avoid having decorator dependencies; when necessary, create a single new decorator that calls both in the right order.

like image 29
dcrosta Avatar answered Oct 04 '22 05:10

dcrosta