Uso de señales de Django para simplificar y desacoplar código

Los sistemas se vuelven más complejos a medida que pasa el tiempo y esto justifica la necesidad de desacoplar más los sistemas. Un sistema desacoplado es más fácil de construir, ampliar y mantener...

Introducción

Los sistemas se vuelven más complejos a medida que pasa el tiempo y esto justifica la necesidad de desacoplar más los sistemas. Un sistema desacoplado es más fácil de construir, ampliar y mantener a largo plazo, ya que el desacoplamiento no solo reduce la complejidad del sistema, sino que cada parte del sistema se puede administrar individualmente. La tolerancia a fallas también ha mejorado ya que, en un sistema desacoplado, un componente defectuoso no arrastra consigo a todo el sistema.

Django es un poderoso marco web de código abierto que se puede usar para construir sistemas grandes y complejos, así como pequeños. Sigue el patrón arquitectónico modelo-plantilla-vista y es fiel a su objetivo de ayudar a los desarrolladores a lograr la entrega de aplicaciones web complejas basadas en datos.

Django nos permite desacoplar la funcionalidad del sistema mediante la creación de aplicaciones separadas dentro de un proyecto. Por ejemplo, podemos tener un sistema de compras y tener aplicaciones separadas que manejen cuentas, envío de recibos por correo electrónico y notificaciones, entre otras cosas.

En dicho sistema, es posible que se necesiten varias aplicaciones para realizar una acción cuando ocurren ciertos eventos. Un evento puede ocurrir cuando un cliente realiza un pedido. Por ejemplo, necesitaremos notificar al usuario por correo electrónico y también enviar el pedido al proveedor o vendedor, al mismo tiempo que podemos recibir y procesar los pagos. Todos estos eventos ocurren al mismo tiempo y dado que nuestra aplicación está desacoplada, debemos mantener todos los componentes sincronizados, pero ¿cómo logramos esto?

Señales Django son útiles en tal situación, todo lo que debe suceder es que se envíe una señal cuando un usuario realiza un pedido, y cada componente relacionado o afectado lo escucha y realiza sus operaciones. Exploremos más sobre las señales en esta publicación.

Señales de un vistazo

Django Signals es una implementación del Patrón de observador. En un patrón de diseño de este tipo, se implementa un mecanismo de suscripción en el que se suscriben múltiples objetos u “observan” un objeto en particular y cualquier evento que pueda ocurrirle. Una buena analogía es cómo todos los suscriptores de un canal de YouTube reciben una notificación cuando un creador de contenido carga contenido nuevo.

A través de un "despachador de señales", Django puede distribuir señales en una configuración desacoplada a "receptores" registrados en los diversos componentes del sistema. Las señales se registran y activan cada vez que ocurren ciertos eventos, y cualquier oyente de ese evento recibirá una notificación de que ocurrió el evento, además de recibir algunos datos contextuales dentro de la carga útil que pueden ser relevantes para la funcionalidad del receptor. Un receptor puede ser cualquier función o método de Python. Más sobre esto más adelante.

Además del despachador de señales, Django también incluye algunas señales útiles que podemos escuchar. Incluyen:

  • post_save, que se envía cada vez que se crea y guarda un nuevo modelo de Django. Por ejemplo, cuando un usuario se registra o sube una nueva publicación,
  • pre_delete, que se envía justo antes de que se elimine un modelo de Django. Un buen escenario sería cuando un usuario está eliminando un mensaje o su cuenta,
  • request_finished, que se activa cada vez que Django completa el servicio de una solicitud HTTP. Esto puede variar desde abrir el sitio web o acceder a un recurso en particular.

Otra ventaja de Django es que es un marco altamente personalizable. En nuestro caso, podemos crear nuestras señales personalizadas y usar el sistema incorporado para enviarlas y recibirlas en nuestro sistema desacoplado. En la sección de demostración, nos suscribiremos a algunas de las señales integradas de Django y también crearemos algunas personalizadas propias.

Pero primero, veamos un ejemplo rápido que utiliza Django Signals. Aquí tenemos dos funciones que juegan al ping-pong entre sí, pero interactúan a través de señales:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from django.dispatch import Signal, receiver

# Create a custom signal
ping_signal = Signal(providing_args=["context"])

class SignalDemo(object):
    # function to send the signal
    def ping(self):
        print('PING')
        ping_signal.send(sender=self.__class__, PING=True)

# Function to receive the signal
@receiver(ping_signal)
def pong(**kwargs):
    if kwargs['PING']:
        print('PONG')

demo = SignalDemo()
demo.ping()

En este sencillo script, hemos creado una clase con un método para enviar la señal y una función separada fuera de la clase que recibirá y responderá. En nuestro caso, el remitente de la señal enviará el comando PING junto con la señal, y la función del receptor verificará si el comando PING está presente e imprimirá PONG en respuesta. La señal se crea con la clase Signal de Django, y es recibida por cualquier función que tenga el @receiver decorador.

La salida del script:

1
2
3
4
$ python signal_demo.py

PING
PONG

Normalmente, tendríamos que invocar la función pong() desde dentro de la función ping(), pero con señales, podemos obtener una solución similar pero desacoplada. La función pong() ahora puede residir en otro proyecto de archivo y seguir respondiendo a nuestra señal PING.

Cuándo usar señales

Ya hemos identificado qué son las señales de Django y cómo funcionan, pero al igual que con cualquier otra característica del marco, no está destinado a ser utilizado en todo momento. Hay escenarios particulares en los que se recomienda encarecidamente que usemos señales de Django, e incluyen:

  • Cuando tenemos muchas piezas de código separadas interesadas en los mismos eventos, una señal ayudaría a distribuir la notificación del evento en lugar de que invoquemos todas las diferentes piezas de código en el mismo punto, lo que puede volverse desordenado e introducir errores.
  • También podemos usar señales de Django para manejar interacciones entre componentes en un sistema desacoplado como alternativa a la interacción a través de mecanismos de comunicación RESTful
  • Las señales también son útiles cuando se amplían bibliotecas de terceros donde queremos evitar modificarlas, pero necesitamos agregar funcionalidades adicionales.

Ventajas de Signals

Django Signals simplifica la implementación de nuestros sistemas desacoplados de varias maneras. Nos ayudan a implementar aplicaciones reutilizables y, en lugar de volver a implementar la funcionalidad por separado o modificar otras partes del sistema, podemos simplemente responder a las señales sin afectar a otro código. De esta manera, los componentes de un sistema se pueden modificar, agregar o eliminar sin tocar el código base existente.

Las señales también proporcionan un mecanismo simplificado para mantener los diferentes componentes de un sistema desacoplado sincronizados y actualizados entre sí.

Proyecto de demostración {#proyecto de demostración}

En nuestro proyecto de demostración, crearemos un tablero de trabajos simple donde los usuarios accederán al sitio, verán los trabajos disponibles y elegirán una publicación de trabajo para suscribirse. Los usuarios se suscribirán simplemente enviando su dirección de correo electrónico y serán notificados de cualquier cambio en el trabajo. Por ejemplo, si los requisitos cambian, la oferta de trabajo se cierra o si se elimina la publicación de trabajo. Todos estos cambios serán realizados por un administrador que tendrá un tablero para crear, actualizar e incluso eliminar ofertas de trabajo.

Con el espíritu de desvincular nuestra aplicación, crearemos la aplicación principal de Bolsa de trabajo y una aplicación de Notificaciones separada que se encargará de notificar a los usuarios cuando sea necesario. Luego usaremos señales para invocar la funcionalidad en la aplicación de Notificaciones desde la aplicación principal de Jobs Board.

Otro testimonio del amplio conjunto de funciones de Django es el panel de administración integrado que nuestros administradores utilizarán para gestionar trabajos. Nuestro trabajo en ese frente se reduce considerablemente y podemos crear prototipos de nuestra aplicación más rápido.

Configuración del proyecto

Es una buena práctica crear proyectos de Python en un entorno virtual para que podamos trabajar en un entorno aislado que no afecte la configuración de Python del sistema, por lo que usaremos Pipenv.

Primero configuremos nuestro entorno:

1
2
3
4
5
6
7
8
# Set up the environment
$ pipenv install --three

# Activate the virtual environment
$ pipenv shell

# Install Django
$ pipenv install django

Django viene con algunos comandos que nos ayudan a realizar varias tareas, como crear un proyecto, crear aplicaciones, migrar datos y probar código, entre otras. Para crear nuestro proyecto:

1
2
3
4
5
6
# Create the project
$ django-admin startproject jobs_board && cd jobs_board

# Create the decoupled applications
$ django-admin startapp jobs_board_main
$ django-admin startapp jobs_board_notifications

Los comandos anteriores crearán un proyecto de Django con dos aplicaciones dentro, que están desacopladas entre sí pero aún pueden funcionar juntas. Para confirmar que nuestra configuración fue exitosa, migremos las migraciones predeterminadas que vienen con Django y configuremos nuestra base de datos y tablas:

1
2
$ python manage.py migrate
$ python manage.py runserver

Cuando accedemos a la instancia de ejecución local de nuestro proyecto Django, deberíamos ver lo siguiente:

django set up

Esto significa que hemos configurado nuestro proyecto Django con éxito y ahora podemos comenzar a implementar nuestra lógica.

Implementación

Django se basa en un patrón de arquitectura modelo-vista-plantilla, y este patrón también guiará nuestra implementación. Crearemos modelos para definir nuestros datos, luego implementaremos vistas para manejar el acceso y la manipulación de datos, y finalmente plantillas para mostrar nuestros datos al usuario final en el navegador.

Para tener nuestras aplicaciones integradas en la aplicación principal de Django, debemos agregarlas a jobs_board/settings.py en INSTALLED_APPS, de la siguiente manera:

1
2
3
4
5
6
7
INSTALLED_APPS = [
    # Existing apps remain...

    # jobs_board apps
    'jobs_board_main',
    'jobs_board_notifications',
]

Parte 1: La aplicación Main Jobs Board

Aquí es donde residirá la mayor parte de la funcionalidad de nuestro sistema y será el punto de interacción con nuestros usuarios. Contendrá nuestros modelos, vistas y plantillas y algunas señales personalizadas que usaremos para interactuar con la aplicación de Notificaciones.

Empecemos creando nuestros modelos en jobs_board_main/models.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# jobs_board_main/models.py

class Job(models.Model):
    company = models.CharField(max_length=255, blank=False)
    company_email = models.CharField(max_length=255, blank=False)
    title = models.CharField(max_length=255, blank=False)
    details = models.CharField(max_length=255, blank=True)
    status = models.BooleanField(default=True)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

class Subscriber(models.Model):
    email = models.CharField(max_length=255, blank=False, unique=True)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

class Subscription(models.Model):
    email = models.CharField(max_length=255, blank=False, unique=True)
    user = models.ForeignKey(Subscriber, related_name="subscriptions", on_delete=models.CASCADE)
    job = models.ForeignKey(Job, related_name="jobs", on_delete=models.CASCADE)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

Creamos un modelo para definir nuestra publicación de ‘Trabajo’, que solo tendrá el nombre de la empresa y los detalles del trabajo junto con el estado de la vacante. También tendremos un modelo para almacenar nuestros suscriptores tomando solo sus direcciones de correo electrónico. Los Suscriptores y los Trabajos se unen a través del modelo de “Suscripción” donde almacenaremos detalles sobre las suscripciones a las publicaciones de Trabajo.

Con nuestros modelos en su lugar, necesitamos hacer migraciones y migrarlos para tener las tablas creadas en la base de datos:

1
2
$ python manage.py makemigrations
$ python manage.py migrate

A continuación pasamos a la sección de vista de nuestra aplicación. Vamos a crear una vista para mostrar todas las ofertas de trabajo y otra para mostrar las ofertas de trabajo individuales donde los usuarios pueden suscribirse enviando sus correos electrónicos.

Comenzaremos creando la vista que manejará la visualización de todos nuestros trabajos:

1
2
3
4
5
6
7
8
# jobs_board_main/views.py

from .models import Job

def get_jobs(request):
    # get all jobs from the DB
    jobs = Job.objects.all()
    return render(request, 'jobs.html', {'jobs': jobs})

Para este proyecto usaremos vistas basadas en funciones, siendo la alternativa vistas basadas en clases, pero eso no es parte de esta discusión. Consultamos la base de datos para todos los trabajos y respondemos a la solicitud especificando la plantilla que representará los trabajos y también incluyendo los trabajos en la respuesta.

Django se envía con el Motor de plantillas Jinja que usaremos para crear los archivos HTML que se entregarán al usuario final. En nuestra aplicación jobs_board_main, crearemos una carpeta templates que alojará todos los archivos HTML que mostraremos a los usuarios finales.

La plantilla para representar todos los trabajos mostrará todos los trabajos con enlaces a ofertas de trabajo individuales, de la siguiente manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!-- jobs_board_main/templates/jobs.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Jobs Board Homepage</title>
  </head>
  <body>
    <h2> Welcome to the Jobs board </h2>

    {% for job in jobs %}
      <div>
        <a href="/jobs/{{ job.id }}">{{ job.title }} at {{ job.company }}</a>
        <p>
          {{ job.details }}
        </p>
      </div>
    {% endfor %}

  </body>
</html>

Hemos creado el modelo Job, la vista get_jobs para obtener y mostrar todas las vistas, y la plantilla para representar la lista de trabajos. Para reunir todo este trabajo, tenemos que crear un punto final desde el cual se pueda acceder a los trabajos, y lo hacemos creando un archivo urls.py en nuestra jobs_board_main_application:

1
2
3
4
5
6
7
8
9
# jobs_board_main/urls.py

from django.urls import path
from .views import get_jobs

urlpatterns = [
    # All jobs
    path('jobs/', get_jobs, name="jobs_view"),
]

En este archivo, importamos nuestra vista, creamos una ruta y le adjuntamos nuestra vista. Ahora registraremos las URL de nuestras aplicaciones en el archivo principal urls.py en la carpeta del proyecto jobs_board:

1
2
3
4
5
6
# jobs_board/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('jobs_board_main.urls')), # <--- Add this line
]

Nuestro proyecto está listo para ser probado ahora. Esto es lo que obtenemos cuando ejecutamos la aplicación y navegamos a localhost:8000/jobs:

jobs board landing 1

Actualmente no tenemos puestos de trabajo en el lugar. Django viene con una aplicación de administración que podemos usar para realizar nuestra entrada de datos. Primero, comenzamos creando un superusuario:

create superuser

Con el superusuario creado, necesitamos registrar nuestros modelos en el archivo admin.py en nuestra aplicación jobs_board_main:

1
2
3
4
5
6
# jobs_board_main/admin.py
from django.contrib import admin
from .models import Job

# Register your models here.
admin.site.register(Job)

Reiniciamos nuestra aplicación y navegamos hasta localhost:8000/admin e iniciamos sesión con las credenciales que acabamos de configurar. Este es el resultado:

jobs board admin 1

Cuando hacemos clic en el signo más en la fila "Empleos", obtenemos un formulario donde completamos los detalles sobre nuestra publicación de trabajo:

jobs data entry

Cuando guardamos el trabajo y navegamos de regreso al punto final trabajos, somos recibidos por la publicación de trabajo que acabamos de crear:

jobs board landing 2

Ahora crearemos las vistas, las plantillas y las URL para mostrar un solo trabajo y también permitiremos que los usuarios se suscriban enviando su correo electrónico.

Nuestro jobs_board_main/views.py se extenderá de la siguiente manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# jobs_board_main/views.py
# previous code remains
def get_job(request, id):
    job = Job.objects.get(pk=id)
    return render(request, 'job.html', {'job': job})

def subscribe(request, id):
    job = Job.objects.get(pk=id)
    sub = Subscriber(email=request.POST['email'])
    sub.save()

    subscription = Subscription(user=sub, job=job)
    subscription.save()

    payload = {
      'job': job,
      'email': request.POST['email']
    }
    return render(request, 'subscribed.html', {'payload': payload})

También necesitaremos crear la plantilla para una vista única de una publicación de trabajo en templates/job.html, que incluye el formulario que tomará el correo electrónico de un usuario y lo suscribirá a la publicación de trabajo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- jobs_board_main/templates/job.html -->
<html>
  <head>
    <title>Jobs Board - {{ job.title }}</title>
  </head>
  <body>
      <div>
        <h3>{{ job.title }} at {{ job.company }}</h3>
        <p>
          {{ job.details }}
        </p>
        <br>
        <p>Subscribe to this job posting by submitting your email</p>
        <form action="/jobs/{{ job.id }}/subscribe" method="POST">
          {% csrf_token %}
          <input type="email" name="email" id="email" placeholder="Enter your email"/>
          <input type="submit" value="Subscribe">
        </form>
        <hr>
      </div>
  </body>
</html>

Una vez que un usuario se suscriba a un trabajo, necesitaremos redirigirlo a una página de confirmación cuya plantilla subscribed.html será la siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!-- jobs_board_main/templates/subscribed.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Jobs Board - Subscribed</title>
  </head>
  <body>
      <div>
        <h3>Subscription confirmed!</h3>
        <p>
          Dear {{ payload.email }}, thank you for subscribing to {{ payload.job.title }}
        </p>
      </div>
  </body>
</html>

Finalmente, nuestra nueva funcionalidad deberá exponerse a través de puntos finales que agregaremos a nuestro jobs_board_main/urls.py existente de la siguiente manera:

1
2
3
4
5
6
7
8
9
# jobs_board_main/urls.py
from .views import get_jobs, get_job, subscribe

urlpatterns = [
    # All jobs
    path('jobs/', get_jobs, name="jobs_view"),
    path('jobs/<int:id>', get_job, name="job_view"),
    path('jobs/<int:id>/subscribe', subscribe, name="subscribe_view"),
]

Ahora podemos probar nuestra aplicación principal de la Junta de Empleos al ver las listas de puestos de trabajo, hacer clic en uno y enviar una dirección de correo electrónico que recibirá actualizaciones.

Ahora que tenemos una aplicación en funcionamiento, es hora de incorporar Django Signals y notificar a los usuarios/suscriptores cuando se produzcan ciertos eventos. Las ofertas de trabajo están vinculadas a una determinada empresa cuyo correo electrónico registramos, queremos notificarles cuando un nuevo usuario se suscriba a su publicación de trabajo. También queremos notificar a los usuarios suscritos cuando se elimine una publicación de trabajo.

Para notificar a los usuarios cuando se elimina o elimina una publicación de trabajo, utilizaremos la señal post_delete incorporada de Django. También crearemos nuestra señal llamada new_subscriber que usaremos para notificar a las empresas cuando los usuarios se suscriban a su publicación de trabajo.

Creamos nuestras señales personalizadas creando un archivo signals.py en nuestra aplicación jobs_board_main:

1
2
3
4
# jobs_board_main/signals.py
from django.dispatch import Signal

new_subscriber = Signal(providing_args=["job", "subscriber"])

¡Eso es todo! Nuestra señal personalizada está lista para ser invocada después de que un usuario se haya suscrito con éxito a una publicación de trabajo de la siguiente manera en nuestro archivo jobs_board_main/views.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# jobs_board_main/views.py

# Existing imports and code are maintained and truncated for brevity
from .signals import new_subscriber

def subscribe(request, id):
    job = Job.objects.get(pk=id)
    subscriber = Subscriber(email=request.POST['email'])
    subscriber.save()

    subscription = Subscription(user=subscriber, job=job, email=subscriber.email)
    subscription.save()

    # Add this line that sends our custom signal
    new_subscriber.send(sender=subscription, job=job, subscriber=subscriber)

    payload = {
      'job': job,
      'email': request.POST['email']
    }
    return render(request, 'subscribed.html', {'payload': payload})

No tenemos que preocuparnos por la señal pre_delete ya que Django la enviará automáticamente justo antes de que se elimine una publicación de trabajo. La razón por la que usamos la señal pre_delete y no post_delete es porque, cuando se elimina un Job, todas las suscripciones vinculadas también se eliminan en el proceso y necesitamos esos datos antes de que también se eliminen.

Ahora consumamos las señales que acabamos de enviar en una aplicación jobs_board_notifications separada.

Parte 2: la aplicación de notificaciones de Jobs Board

Ya creamos la aplicación jobs_board_notifications y la conectamos a nuestro proyecto Django. En esta sección, consumiremos las señales enviadas desde nuestra aplicación principal y enviaremos las notificaciones. Django tiene una funcionalidad integrada para enviar correos electrónicos, pero para fines de desarrollo, imprimiremos los mensajes en la consola.

Nuestra aplicación jobs_board_notifications no necesita la interacción del usuario, por lo tanto, no necesitamos crear ninguna vista o plantilla para ese propósito. El único objetivo de nuestras jobs_board_notifications es recibir señales y enviar notificaciones. Implementaremos esta funcionalidad en nuestro models.py ya que se importa antes cuando se inicia la aplicación.

Recibamos nuestras señales en nuestro jobs_board_notifications/models.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# jobs_board_notifications/models.py.
from django.db.models.signals import pre_delete
from django.dispatch import receiver

from jobs_board_main.signals import new_subscriber
from jobs_board_main.models import Job, Subscriber, Subscription

@receiver(new_subscriber, sender=Subscription)
def handle_new_subscription(sender, **kwargs):
    subscriber = kwargs['subscriber']
    job = kwargs['job']

    message = """User {} has just subscribed to the Job {}.
    """.format(subscriber.email, job.title)

    print(message)

@receiver(pre_delete, sender=Job)
def handle_deleted_job_posting(**kwargs):
    job = kwargs['instance']

    # Find the subscribers list
    subscribers = Subscription.objects.filter(job=job)

    for subscriber in subscribers:
        message = """Dear {}, the job posting {} by {} has been taken down.
        """.format(subscriber.email, job.title, job.company)

        print(message)

En nuestras jobs_board_notifications, importamos nuestra señal personalizada, la señal pre_save y nuestros modelos. Utilizando @receiver decorador, capturamos las señales y los datos contextuales pasados ​​con ellas como argumentos de palabras clave.

Al recibir los datos contextuales, los usamos para enviar los "correos electrónicos" (recuerde que solo estamos imprimiendo en la consola en aras de la simplicidad) a suscriptores y empresas cuando un usuario se suscribe y una publicación de trabajo es eliminada por respondiendo a las señales que enviamos.

Pruebas

Una vez que hemos creado un trabajo en nuestro panel de administración, está disponible para que los usuarios se suscriban. Cuando los usuarios se suscriben, se envía el siguiente correo electrónico desde la aplicación jobs_board_notifications a la empresa propietaria de la publicación:

job subscribe message

Esta es una prueba de que nuestra señal new_subscriber fue enviada desde la aplicación jobs_board_main y recibida por la aplicación jobs_board_notifications.

Cuando se elimina una publicación de trabajo, todos los usuarios que se suscribieron a la publicación de trabajo reciben una notificación por correo electrónico, de la siguiente manera:

job deleted email

La señal pre_delete de Django fue útil y nuestro controlador envió notificaciones a los usuarios suscritos de que la publicación de trabajo en particular había sido eliminada.

Resumen

En este artículo hemos construido un proyecto Django con dos aplicaciones que se comunican a través de Django Signals en respuesta a ciertos eventos. Nuestras dos aplicaciones están desacopladas y la complejidad de la comunicación entre nuestras aplicaciones se ha reducido considerablemente. Cuando un usuario se suscribe a una oferta de trabajo, notificamos a la empresa. A su vez, cuando se elimina una publicación de trabajo, notificamos a todos los clientes suscritos que la publicación de trabajo se eliminó.

Sin embargo, hay algunas cosas que debemos tener en cuenta al utilizar Django Signals. Cuando las señales no están bien documentadas, los nuevos mantenedores pueden tener dificultades para identificar la causa raíz de ciertos problemas o comportamientos inesperados. Por lo tanto, cuando se utilizan señales en una aplicación, es una buena idea documentar las señales utilizadas, dónde se reciben y la razón detrás de ellas. Esto ayudará a cualquier persona que mantenga el código a comprender el comportamiento de la aplicación y resolver problemas más rápido y mejor. Además, es útil tener en cuenta que las señales se envían sincrónicamente. No se ejecutan en segundo plano ni mediante trabajos asincrónicos.

Con toda esta información sobre las señales de Django y el proyecto de demostración, deberíamos poder aprovechar el poder de las señales en nuestros proyectos web de Django.

El código fuente de este proyecto está disponible aquí en Github.