Django incluye un «dispatcher de señales» que ayuda a las aplicaciones decoupled a ser notificadas cuando ocurren acciones en otras partes del marco. En resumen, las señales permiten ciertos emisores notificar a un conjunto de receptores que ha ocurrido alguna acción. Son especialmente útiles cuando muchos fragmentos de código pueden estar interesados en los mismos eventos.
Por ejemplo, una aplicación de terceros puede registrarse para ser notificada de cambios de configuración:
from django.apps import AppConfig
from django.core.signals import setting_changed
def my_callback(sender, **kwargs):
print("Setting changed!")
class MyAppConfig(AppConfig):
...
def ready(self):
setting_changed.connect(my_callback)
Las señales de Django integradas permiten al código del usuario obtener notificaciones de ciertas acciones.
También puedes definir y enviar tus propias señales personalizadas. Consulta Definir y enviar señales a continuación.
Advertencia
Las señales dan la apariencia de acoplamiento suelto, pero pueden llevar rápidamente al código que es difícil de entender, ajustar y depurar.
Donde sea posible debes optar por llamar directamente al código de manejo en lugar de enviarlo mediante un señal.
Para recibir una señal, registra una función receptor utilizando el método Signal.connect(). La función receptor se llama cuando se envía la señal. Todas las funciones receptor de la señal se llaman uno a la vez, en el orden en que fueron registradas.
receiver – La función de llamada de retorno que se conectará a esta señal. Consulte Funciones receptoras para obtener más información.
sender – Especifica un emisor particular para recibir señales desde él. Consulte Conectando a señales enviadas por emisores específicos para obtener más información.
weak – Django almacena los manejadores de señal como referencias débiles por defecto. Por lo tanto, si tu receptor es una función local, puede ser recogido por la basura. Para evitar esto, pasa weak=False cuando llames al método connect() de la señal.
dispatch_uid – Un identificador único para un receptor de señal en casos donde pueden enviarse señales duplicadas. Consulte Evitar señales duplicadas para obtener más información.
Vamos a ver cómo funciona registrando una señal que se llama después de cada solicitud HTTP terminada. Conectaremos a la señal request_finished.
Primero, debemos definir una función receptor. Un receptor puede ser cualquier función o método de Python:
def my_callback(sender, **kwargs):
print("Request finished!")
Los textos traducidos son:
Vamos a ver los emisores un poco más adelante, pero por ahora mira el argumento **kwargs. Todas las señales envían argumentos de palabra clave, y pueden cambiar esos argumentos en cualquier momento. En el caso de request_finished, está documentado como enviando ningún argumento, lo que significa que podríamos estar tentados a escribir nuestro manejador de señal como my_callback(sender).
Esto sería incorrecto – en realidad, Django lanzará un error si lo haces así. Eso es porque en cualquier momento se pueden agregar argumentos a la señal y tu receptor debe poder manejar esos nuevos argumentos.
Los receptores también pueden ser funciones asíncronas, con el mismo firmado pero declaradas utilizando async def:
async def my_callback(sender, **kwargs):
await asyncio.sleep(5)
print("Request finished!")
Las señales se pueden enviar de manera síncrona o asíncrona, y los receptores se adaptarán automáticamente al estilo de llamada correcto. Consulta sending signals para obtener más información.
Hay dos formas en que puedes conectar una función a una señal. Puedes tomar la ruta de conexión manual:
from django.core.signals import request_finished
request_finished.connect(my_callback)
Alternativamente, puedes utilizar un decorador receiver():
signal – Una señal o una lista de señales para conectar una función.
kwargs – Argumentos de palabra clave wildcard para pasar a una función.
Aquí está cómo conectarte con el decorador:
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
Ahora, nuestra función my_callback se llamará cada vez que termine una solicitud.
¿Dónde debería vivir este código?
Habilidades de manejo de señales y registro del código pueden vivir en cualquier lugar que desees, aunque se recomienda evitar el módulo raíz de la aplicación y su módulo models para minimizar los efectos laterales al importar el código.
En la práctica, los manejadores de señales suelen definirse en un módulo signals del aplicativo al que se relacionan. Los receptores de señal se conectan en el método ready() de tu aplicación configuración de la clase. Si estás utilizando el decorador receiver(), importa el módulo signals dentro del método ready(), lo que conectará los manejadores de señal de manera implícita:
from django.apps import AppConfig
from django.core.signals import request_finished
class MyAppConfig(AppConfig):
...
def ready(self):
# Implicitly connect signal handlers decorated with @receiver.
from . import signals
# Explicitly connect a signal handler.
request_finished.connect(signals.my_callback)
Nota
El método ready() puede ejecutarse más de una vez durante la prueba, por lo que podrías querer proteger tus señales contra duplicados, especialmente si planeas enviarlas dentro de las pruebas.
Algunas señales se envían muchas veces, pero solo estarás interesado en recibir un subconjunto específico de esas señales. Por ejemplo, considera la señal django.db.models.signals.pre_save que se envía antes de que un modelo se guarde. La mayoría del tiempo, no necesitas saber cuando cualquier modelo se guarde – solo cuando uno específico se guarde.
En estos casos, puedes registrarte para recibir señales enviadas solo por determinados emisores. En el caso de :data:`django.db.models.signals.pre_save, el emisor será la clase del modelo que se está guardando, así que puedes indicar que solo quieres señales enviadas por algún modelo:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs): ...
La función my_handler solo se llamará cuando se guarde una instancia de MyModel.
Different signals use different objects as their senders; tendrás que consultar la documentación de señales integrada <ref/signals> para detalles sobre cada señal en particular.
En algunas circunstancias, el código que conecta receptores a señales puede ejecutarse varias veces. Esto puede causar que tu función receptor se registre más de una vez y así llamada tantas veces para un evento de señal. Por ejemplo, el método ready() puede ejecutarse más de una vez durante la prueba. De manera general, esto ocurre en todas las partes de tu proyecto donde importes el módulo donde definís las señales, porque la registro de señales se ejecuta tantas veces como se importa.
Si este comportamiento es problemático (como cuando usas señales para enviar un correo electrónico cada vez que se guarde un modelo), pasa un identificador único como argumento dispatch_uid para identificar tu función receptor. Este identificador suele ser una cadena, aunque cualquier objeto hashable servirá. El resultado final es que tu función receptor solo estará ligada a la señal una vez por cada valor único de dispatch_uid:
from django.core.signals import request_finished
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
Tu aplicación puede aprovechar la infraestructura de señales y proporcionar sus propias señales.
Cuándo usar señales personalizadas
Las señales son llamadas a funciones implícitas que hacen más difícil el depurado. Si el emisor y receptor de tu señal personalizada están ambos dentro de tu proyecto, te conviene usar una llamada a función explícita.
Todas las señales son instancias de django.dispatch.Signal.
Por ejemplo:
import django.dispatch
pizza_done = django.dispatch.Signal()
Esta declara un señal pizza_done.
Hay dos formas de enviar señales sincrónicamente en Django.
Las señales también pueden enviarse de manera asíncrona.
Para enviar una señal, llama a cualquiera de los métodos Signal.send(), Signal.send_robust(), await Signal.asend(), o await Signal.asend_robust(). Debes proporcionar el argumento sender (que es una clase la mayoría de las veces) y puedes proporcionar tantos otros argumentos clave como desees.
Por ejemplo, aquí está cómo enviar nuestra señal pizza_done podría verse así:
class PizzaStore:
...
def send_pizza(self, toppings, size):
pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
...
Todas las cuatro formas devuelven una lista de pares de tuplas [(receiver, response), ...], representando la lista de funciones receptor llamadas y sus valores de respuesta.
send() difiere de send_robust() en cómo se manejan las excepciones levantadas por las funciones receptor. send() no captura ninguna excepción levantada por los receptores; simplemente permite que los errores propaguen. Por lo tanto, no todos los receptores pueden ser notificados de una señal en presencia de un error.
send_robust() captura todos los errores derivados de la clase Exception de Python y asegura que todos los receptores sean notificados de la señal. Si ocurre un error, el objeto de error se devuelve en el par de tuplas para el receptor que levantó el error.
Los rastros de llamada están presentes en el atributo __traceback__ de los errores devueltos al llamar a send_robust().
asend() es similar a send(), pero es un coroutine que debe ser esperado:
async def asend_pizza(self, toppings, size):
await pizza_done.asend(sender=self.__class__, toppings=toppings, size=size)
...
Ya sea sincrónico o asíncrono, los receptores se adaptarán correctamente según si se utiliza send() o asend(). Los receptores sincrónicos se llamarán utilizando sync_to_async() cuando se invoquen a través de asend(). Los receptores asíncronos se llamarán utilizando async_to_sync() cuando se invoquen a través de send(). De manera similar al caso para middleware <async_performance>, hay un pequeño costo de rendimiento en adaptar los receptores de esta forma. Tenga en cuenta que, con el fin de reducir el número de cambios entre sincrónico y asíncrono dentro de una llamada a send() o asend(), los receptores se agrupan según si son async antes de ser llamados. Esto significa que un receptor asíncrono registrado antes que un receptor sincrónico puede ejecutarse después del receptor sincrónico. Además, los receptores async se ejecutan de manera concurrente utilizando asyncio.gather().
Todos los señales integradas, excepto aquellas en el ciclo de solicitud-respuesta asíncrona, se envían utilizando Signal.send().
Para desconectar un receptor de una señal, llame a Signal.disconnect(). Los argumentos son descritos en Signal.connect(). El método devuelve True si se desconectó un receptor y False si no. Cuando sender es pasado como referencia lazy a <app label>.<model>, este método siempre devuelve None.
El argumento receiver indica el receptor registrado que debe desconectar. Puede ser None si se utiliza dispatch_uid para identificar al receptor.
may 31, 2026