Predefined Signals in Django


In Django, signals allow certain senders to notify a set of receivers when certain actions have taken place. They are used to allow decoupled applications to get notified when certain events occur. Django provides several predefined signals that can be used to trigger actions during various phases of request/response processing.

1. What Are Signals in Django?

Signals are a form of event-driven programming where certain actions (like saving a model, user login, etc.) trigger corresponding functions. Django's signals framework allows different parts of a Django application to communicate with each other by sending and receiving notifications.

Signals are dispatched using the signal.send() method, and receivers can listen to and handle these signals using decorators like @receiver.

2. Predefined Django Signals

Django provides several built-in signals that are triggered at different points in the application's lifecycle. Below are some of the most commonly used predefined signals in Django:

2.1. pre_save and post_save Signals

The pre_save signal is sent just before a model’s save() method is called. The post_save signal is sent right after the model’s save() method is executed.

Example: Using pre_save and post_save

            
    # models.py
    from django.db import models
    from django.db.models.signals import pre_save, post_save
    from django.dispatch import receiver

    class Author(models.Model):
        name = models.CharField(max_length=100)
        email = models.EmailField()

    # Receiver for pre_save signal
    @receiver(pre_save, sender=Author)
    def pre_save_handler(sender, instance, **kwargs):
        print("Preparing to save author:", instance.name)

    # Receiver for post_save signal
    @receiver(post_save, sender=Author)
    def post_save_handler(sender, instance, created, **kwargs):
        if created:
            print("New author created:", instance.name)
        else:
            print("Author updated:", instance.name)
            
        

In this example, the pre_save_handler will execute before saving an Author instance, and the post_save_handler will execute after the Author instance is saved.

2.2. pre_delete and post_delete Signals

Similar to the save signals, the pre_delete and post_delete signals are triggered just before and after an object is deleted from the database, respectively.

Example: Using pre_delete and post_delete

            
    # models.py
    from django.db import models
    from django.db.models.signals import pre_delete, post_delete
    from django.dispatch import receiver

    class Book(models.Model):
        title = models.CharField(max_length=200)
        author = models.ForeignKey('Author', on_delete=models.CASCADE)

    # Receiver for pre_delete signal
    @receiver(pre_delete, sender=Book)
    def pre_delete_handler(sender, instance, **kwargs):
        print("Preparing to delete book:", instance.title)

    # Receiver for post_delete signal
    @receiver(post_delete, sender=Book)
    def post_delete_handler(sender, instance, **kwargs):
        print("Book deleted:", instance.title)
            
        

The pre_delete_handler is triggered just before a Book instance is deleted, while the post_delete_handler is triggered after it is deleted.

2.3. m2m_changed Signal

The m2m_changed signal is sent when a many-to-many (M2M) relationship is modified. It is triggered when items are added or removed from a many-to-many field.

Example: Using m2m_changed

            
    # models.py
    from django.db import models
    from django.db.models.signals import m2m_changed
    from django.dispatch import receiver

    class Tag(models.Model):
        name = models.CharField(max_length=50)

    class Post(models.Model):
        title = models.CharField(max_length=200)
        tags = models.ManyToManyField(Tag)

    # Receiver for m2m_changed signal
    @receiver(m2m_changed, sender=Post.tags.through)
    def m2m_changed_handler(sender, instance, action, **kwargs):
        if action == 'post_add':
            print(f"Tags added to post: {instance.title}")
        elif action == 'post_remove':
            print(f"Tags removed from post: {instance.title}")
            
        

Here, the signal handler listens to changes in the tags many-to-many field of the Post model. It prints a message whenever tags are added or removed from a post.

2.4. request_started and request_finished Signals

The request_started signal is sent just before Django starts processing a request, while the request_finished signal is sent after Django finishes processing a request.

Example: Using request_started and request_finished

            
    # signals.py
    from django.core.signals import request_started, request_finished
    from django.dispatch import receiver
    import time

    @receiver(request_started)
    def request_started_handler(sender, **kwargs):
        print("Request started at:", time.time())

    @receiver(request_finished)
    def request_finished_handler(sender, **kwargs):
        print("Request finished at:", time.time())
            
        

This example logs the time at which each request starts and finishes, which can be helpful for debugging and performance monitoring.

3. Connecting and Disconnecting Signals

To connect a signal to a receiver function, you can use the @receiver decorator or manually connect it using the signal.connect() method. You can also disconnect a signal using signal.disconnect().

            
    # Manual signal connection
    from django.db.models.signals import pre_save
    from myapp.models import Author
    from myapp.signals import pre_save_handler

    pre_save.connect(pre_save_handler, sender=Author)

    # Disconnect signal
    pre_save.disconnect(pre_save_handler, sender=Author)
            
        

4. Conclusion

Predefined signals in Django are an important tool for allowing different parts of your application to communicate with each other. By using signals, you can easily hook into various lifecycle events such as saving, deleting models, handling many-to-many relationships, and processing requests. Understanding how to work with Django’s signals system can greatly improve the flexibility and maintainability of your application.





Advertisement