Principios y Cómo Escribir Decoradores Personalizados de Django
Un decorador es una herramienta poderosa en Django que permite agregar fácilmente lógica común a funciones o clases de vista. En este artículo, explicaré el principio de funcionamiento y cómo escribir decoradores paso a paso, para que puedas crear tu propio decorador personalizado.
1. ¿Qué es un decorador?
Un decorador es una función de orden superior en Python. Es decir, es una función que toma otra función como argumento y devuelve una nueva función dentro de ella.
- Funciones de los decoradores:
- Puede envolver una función para agregar o modificar características.
- Facilita la reutilización y concisión del código.
2. Principio básico de funcionamiento del decorador
Para entender el funcionamiento de un decorador, veamos un ejemplo simple.
def simple_decorator(func):
def wrapper():
print("Antes de que se ejecute la función")
func()
print("Después de que se ejecute la función")
return wrapper
@simple_decorator
def say_hello():
print("¡Hola!")
say_hello()
Resultado de la salida:
Antes de que se ejecute la función
¡Hola!
Después de que se ejecute la función
Explicación:
@simple_decorator
pasa la funciónsay_hello
al decoradorsimple_decorator
.simple_decorator
crea y devuelve una nueva funciónwrapper
.- Cuando se llama a
say_hello
, se ejecutawrapper
en lugar de la función original. - Dentro de
wrapper
, se ejecutan comportamientos adicionales (mensajes de impresión) y se llama a la función original.
3. Principios de escritura de decoradores personalizados en Django
Para escribir un decorador en Django, es necesario manejar el objeto request que es un argumento de las funciones de vista. Con esto se puede manipular la respuesta HTTP o ejecutar lógica según ciertas condiciones.
4. Estructura paso a paso para escribir un decorador personalizado
A continuación, se muestra la estructura básica para escribir un decorador personalizado en Django:
- Uso de
functools.wraps
: Se usa@wraps
para mantener los metadatos de la función original, como el nombre y la cadena de documentación. - Agregar lógica basada en
request
: Escribe lógica para analizar o validar el objeto de solicitud. - Llamar a la función original o devolver una respuesta alternativa: Según ciertas condiciones, se puede bloquear la llamada a la función original y devolver una respuesta diferente.
A continuación se muestra el código de ejemplo:
from functools import wraps
from django.http import HttpResponseForbidden
def custom_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# Verificación de usuario en el objeto de solicitud
if not request.user.is_authenticated:
return HttpResponseForbidden("Debes estar logueado para acceder a esta página.")
# Llamar a la función de vista original
return view_func(request, *args, **kwargs)
return _wrapped_view
5. ¿Cómo agregar parámetros a un decorador?
Si un decorador necesita parámetros, se debe utilizar una estructura que envuelva la función dos veces. Esto se hace porque la primera función se utiliza para manejar los parámetros del decorador, mientras que la segunda función es la que envuelve la función de vista real.
Estructura básica:
def decorator_with_params(param1, param2):
def actual_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# Lógica utilizando los parámetros param1, param2
return view_func(request, *args, **kwargs)
return _wrapped_view
return actual_decorator
Explicación:
- La primera función
decorator_with_params
maneja los parámetros del decorador (param1
,param2
). - Devuelve un decorador interno que envuelve la función de vista real (
actual_decorator
). - Dentro de
actual_decorator
se llama a la función de vista original o se ejecuta la lógica adicional.
6. Ejemplo en la práctica: Decorador de acceso restringido por horarios
A continuación, se presenta un ejemplo de decorador que solo permite el acceso en ciertos horarios. Este decorador recibe como parámetros los horarios permitidos.
from datetime import datetime
from django.http import HttpResponseForbidden
def time_restricted_access(start_hour, end_hour):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
current_hour = datetime.now().hour
if not (start_hour <= current_hour < end_hour):
return HttpResponseForbidden("Esta página solo es accesible durante ciertas horas.")
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
@time_restricted_access(9, 17) # Permitido de 9 AM a 5 PM
def working_hours_view(request):
return render(request, 'working_hours.html')
Descripción del principio de funcionamiento:
time_restricted_access
es la primera función que maneja los parámetros (start_hour
,end_hour
).- La primera función devuelve un decorador interno que envuelve la vista.
- El decorador recibe el
request
, comprueba la hora actual (datetime.now().hour
) y según la condición temporal:- Si se satisface la condición, llama a la vista y devuelve el resultado.
- Si no se satisface la condición, lo bloquea con
HttpResponseForbidden
.
¿Por qué envolver la función dos veces?
El motivo de la doble envoltura es separar las responsabilidades de manejo de parámetros y la función de vista.
- Primera función: recibe los parámetros (
start_hour
,end_hour
) y genera el decorador interno. - Segunda función: el decorador generado envuelve la función de vista y opera sobre el request.
Esta estructura hace que el decorador sea flexible y reutilizable.
7. Usar en vistas basadas en clases (CBV)
Para las vistas basadas en clases, es necesario utilizar method_decorator
.
from django.utils.decorators import method_decorator
from django.views import View
@method_decorator(time_restricted_access(9, 17), name='dispatch')
class MyView(View):
def get(self, request):
return render(request, 'my_template.html')
8. Conclusión: Consideraciones al escribir decoradores personalizados
- Mantener la simplicidad: La lógica del decorador debe ser simple y clara.
- Considerar la reutilización: Si cierta lógica se utiliza en múltiples vistas, extráela en un decorador.
- Manejo de parámetros: Usa la estructura de doble envoltura cuando necesites parámetros.
- Preservación de metadatos: Utiliza
@wraps
para mantener los metadatos de la función original.
Conclusión
Ahora entiendes el principio de funcionamiento interno de los decoradores personalizados de Django y cómo escribir decoradores que acepten parámetros. ¡Utiliza este artículo como base para crear decoradores que se ajusten a tu proyecto!
Add a New Comment