Discover how to automate tasks around saving data without cluttering your code!
Why pre_save and post_save signals in Django? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you have a website where users can upload profiles. Every time a profile is saved, you want to send a welcome email and update some statistics manually by adding code everywhere you save the profile.
Manually adding code to send emails or update stats in every save location is easy to forget, leads to duplicated code, and makes your app hard to maintain and debug.
Django's pre_save and post_save signals let you run code automatically before or after any model is saved, keeping your logic clean and centralized.
def save_profile(profile):
profile.save()
send_welcome_email(profile)
update_stats(profile)from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender=Profile) def send_email(sender, instance, created, **kwargs): if created: send_welcome_email(instance)
You can add extra actions tied to saving data without changing your main save code, making your app easier to grow and maintain.
When a new blog post is saved, automatically update the author's post count and notify followers without touching the blog post save logic.
Manually handling save-related tasks causes repeated and fragile code.
pre_save and post_save signals run code automatically around saving.
This keeps your app organized and easier to update.
Practice
pre_save signal in Django?Solution
Step 1: Understand the timing of
Thepre_savepre_savesignal triggers just before saving a model instance to the database.Step 2: Compare with other signals
post_saveruns after saving, and deletion signals run on delete, so they don't matchpre_save.Final Answer:
To run code before a model instance is saved to the database -> Option BQuick Check:
pre_save= before save [OK]
- Confusing pre_save with post_save
- Thinking pre_save runs after saving
- Mixing signals with form validation
post_save signal to a model named Book?Solution
Step 1: Recall the signal connection syntax
The correct syntax issignal.connect(handler_function, sender=ModelClass).Step 2: Match the syntax to options
post_save.connect(my_handler, sender=Book) matches this pattern exactly, connectingmy_handlertopost_saveforBook.Final Answer:
post_save.connect(my_handler, sender=Book) -> Option AQuick Check:
Signal.connect(handler, sender=Model) = correct [OK]
- Swapping handler and sender arguments
- Trying to call connect on the model
- Using incorrect order of parameters
Author instance is saved?from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
@receiver(pre_save, sender=Author)
def before_save(sender, instance, **kwargs):
print('Before saving:', instance.name)
@receiver(post_save, sender=Author)
def after_save(sender, instance, created, **kwargs):
if created:
print('Created:', instance.name)
else:
print('Updated:', instance.name)
# Assume instance.name = 'Alice' and this is a new saveSolution
Step 1: Understand signal order on save
pre_saveruns before saving, so it prints 'Before saving: Alice' first.Step 2: Check post_save behavior for new instance
post_saveruns after saving; sincecreated=True, it prints 'Created: Alice'.Final Answer:
Before saving: Alice Created: Alice -> Option CQuick Check:
pre_save then post_save with created=True = Before saving: Alice Created: Alice [OK]
- Assuming post_save runs before pre_save
- Ignoring the created flag in post_save
- Mixing update and create messages
Product). What is the likely problem?from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save)
def product_saved(sender, instance, **kwargs):
print('Product saved:', instance.name)Solution
Step 1: Check the @receiver decorator usage
The@receiver(post_save)decorator needs asender=ModelClassargument to connect specifically toProduct.Step 2: Understand why handler runs for all
Withoutsender=Product, the handler listens topost_savefor all models, causing it to run whenever any model is saved.Final Answer:
Missing sender argument in @receiver decorator -> Option AQuick Check:
@receiver(post_save, sender=Model) required [OK]
- Omitting sender argument in @receiver
- Assuming handler name matters
- Thinking post_save can't be used with certain models
slug field based on its title before saving, but only if the slug is empty. Which signal and approach is best?Solution
Step 1: Identify when to set slug
The slug should be set before saving to ensure it is stored correctly in the database.Step 2: Choose the right signal
pre_saveruns before saving, allowing modification of fields likeslugbefore the database write.Step 3: Why not post_save or save override
post_saveruns after saving, so changing slug then requires another save. Overridingsave()is possible butpre_savekeeps logic separate and clean.Final Answer:
Use pre_save signal to check if slug is empty and set it from title -> Option DQuick Check:
Set fields before save with pre_save [OK]
- Setting slug after saving causing extra saves
- Overriding save() but setting slug too late
- Using form validation which may not cover all saves
