When developing with Django, you often encounter situations where you want to perform an action when a certain event occurs. For example, you might want to log every time a specific model is saved, or automatically update related data when a user profile is updated. In such cases, Django Signals are very useful. Signals create an event-driven structure that allows you to perform necessary actions while keeping the coupling between your code loose.
In this post, we will explore how to utilize the most commonly used Django Signals: pre_save
and post_save
.

1. Overview of Django Signals
Django Signals provide the functionality to automatically call predefined functions when specific events occur in your application. Developers can define their own signals, but Django provides built-in signals such as pre_save
, post_save
, pre_delete
, and post_delete
. These signals are triggered when a model instance is saved or deleted, making them especially useful for database-related actions.
Tip: When setting up signals, it is important to clearly specify the type of signal and the target (model) to avoid unexpected errors.
2. Differences between pre_save
and post_save
pre_save
: Executes before the model instance is saved to the database.post_save
: Executes after the model instance is saved to the database.
These two signals fire at different times, so you need to choose the appropriate one depending on what task you want to perform. For example, if you need to change a value before saving, use pre_save
. If you want to perform another action after saving, post_save
is more suitable.
3. Example of Using pre_save
Now let's take a look at how to use the pre_save
signal in practice. For instance, let's say we want to automatically convert a username to lowercase before saving it.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import UserProfile
@receiver(pre_save, sender=UserProfile)
def lowercase_username(sender, instance, **kwargs):
instance.username = instance.username.lower()
In the code above, the @receiver
decorator connects the pre_save
signal to the lowercase_username
function. Now, this function will be automatically called before saving a UserProfile
model instance, converting the username
field to lowercase.
Tip: The
pre_save
signal is useful for tasks such as data validation or field value transformation before the data enters the database.
Common Mistakes with pre_save
One of the most common mistakes when using the pre_save
signal is calling the save
method again. For instance, if you accidentally call instance.save()
while updating a specific field before saving, you can end up in an infinite loop. Be careful not to call save()
again within the signal handling function.
4. Example of Using post_save
Now let's explore using the post_save
signal. Suppose we want to send a welcome email when a user signs up. In this case, the post_save
signal is very useful.
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import UserProfile
@receiver(post_save, sender=UserProfile)
def send_welcome_email(sender, instance, created, **kwargs):
if created: # Only send the email if the user was newly created
send_mail(
'Welcome!',
'Thank you for signing up!',
'from@example.com',
[instance.email],
fail_silently=False,
)
Here, we've set up the function to send an email only if the created
parameter indicates a new object. The post_save
signal is triggered after the object has been saved to the database, making it ideal for performing follow-up actions and verifying data.
Example of Utilizing post_save
: Updating Related Models
The post_save
signal is usually used when you need to perform additional tasks after a model has been saved. For instance, you could use it to automatically update the counts of tags and categories after saving a blog post or log changes to product stock.
from .models import BlogPost, Category
@receiver(post_save, sender=BlogPost)
def update_category_count(sender, instance, created, **kwargs):
if created:
category = instance.category
category.post_count = BlogPost.objects.filter(category=category).count()
category.save()
The above example updates the post_count
of the related category each time a new BlogPost
instance is saved. Using the post_save
signal allows you to dynamically modify associated data after saving.
5. Considerations When Using pre_save
and post_save
- Avoid Infinite Loops: Be cautious not to call
save()
again within the signal handling function. Callingsave()
will trigger thepre_save
andpost_save
signals again, leading to an infinite loop. - Conditional Processing: It's good practice to set up your signals to process only under certain conditions. For example, in
post_save
, you can differentiate between newly created and updated objects by using thecreated
parameter. - Location of Signal Registration: The location of signal registration is also important. Generally, it's best to register signals inside the
ready()
method of theapps.py
file or create a separatesignals.py
file for management. Registering signals in multiple places may lead to unexpected behavior.
In Conclusion
The pre_save
and post_save
signals in Django enable you to execute various tasks before and after storing data. By utilizing signals, you can improve development efficiency and code flexibility by going beyond simply saving data—such as validating data before saving or updating relationships with other models after saving.
In the next post, we'll look at the pre_delete
and post_delete
signals, discussing various use cases for operations at the deletion phase. Signals are a tool that makes Django development more interesting and efficient, so be sure to apply them appropriately!
Add a New Comment