python - urls - render django
Múltiples decoradores para una vista en Django: Orden de ejecución (4)
Ahora, los decoradores en Python trabajan de adentro hacia afuera.
Bueno, supongo que eso depende de tu definición de adentro hacia afuera. en su caso, login_required
debe ejecutar login_required
, por lo que debe ser el decorador "más externo" (arriba)
Como notaste, tu último ejemplo funciona, y de hecho es la forma correcta de hacerlo.
editar
Tal vez la confusión está en cómo (estos particulares) trabajan los decoradores.
login_required(original_view)
devuelve una vista nueva, que primero comprueba si ha iniciado sesión y luego llama a original_view
asi que
login_required(
active_required(
my_view
)
)
first checks if you are logged in, then
first(second) checks if you are active, then
runs my_vew
Estoy tratando de decorar una vista de Django por dos decoradores, uno para verificar el inicio de sesión y otro para verificar is_active.
El primero es el @login_required
, y el segundo es el siguiente:
def active_required(function):
dec = user_passes_test(lambda u: u.is_active, ''/notallowed'', '''')
return dec(function)
Ahora, los decoradores en Python trabajan al revés, sin embargo, lo siguiente no funciona:
@active_required
@login_required
def foo(request):
...
Primero quiero verificar si el usuario está conectado y redireccionar a la página de inicio de sesión de no ser así, y si él o ella están conectados, quiero verificar si él o ella están activos, y si no, realizar la redirección a ''/notallowed''
.
Lo que sucede es que si el login_required falla, el usuario no es redirigido a la página de inicio de sesión, pero se ejecuta @active_required
, y dado que el usuario es nulo en ese caso, @active_required decorator falla y el usuario es redirigido a /notallowed
.
Cambiar el orden parece funcionar,
@login_required
@active_required
def foo(request):
...
pero sospecho que hay algo malo con este enfoque también.
¿Cuál es la manera correcta de combinar dos decoradores y por qué el orden de ejecución difiere de los decoradores Python simples?
Los decoradores se aplican en el orden en que aparecen en la fuente. Por lo tanto, su segundo ejemplo:
@login_required
@active_required
def foo(request):
...
es equivalente a lo siguiente:
def foo(request):
...
foo = login_required(active_required(foo))
Por lo tanto, si el código de un decorador depende de algo establecido por (o asegurado por) otro, debe colocar el decorador dependiente "dentro" del decorador sin bordes.
Sin embargo, como señala Chris Pratt, debe evitar tener dependencias de decorador; cuando sea necesario, cree un nuevo decorador que llame a ambos en el orden correcto.
Para explicarlo un poco más (también estaba confundido al principio): active_required
se aplica primero en el sentido de que toma my_view
y lo envuelve en algún código. Luego se aplica login_required
y envuelve el resultado en un código más.
Pero cuando en realidad se invoca esta versión my_view
de my_view
, primero se ejecuta el código agregado por login_required
(verificando que haya iniciado sesión), luego se ejecuta el código agregado por active_required
(verificando que esté activo) y finalmente my_view
es ejecutado.
Solo tiene sentido apilar decoradores si tienen una funcionalidad realmente única. Según su descripción, nunca habrá un escenario en el que desee utilizar active_required
pero no login_required
. Por lo tanto, tiene más sentido tener un decorador login_and_active_required
que verifique ambos y las sucursales en consecuencia. Menos para escribir, menos para documentar, y niega el problema.