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?
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