Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Using Django pre_save and post_save Signals
📖 Scenario: You are building a simple Django app to manage books in a library. You want to automatically update a timestamp before saving a book and log a message after saving it.
🎯 Goal: Create a Django model for Book and use pre_save and post_save signals to update a field and print a message.
📋 What You'll Learn
Create a Book model with fields title (string) and last_updated (DateTimeField).
Create a pre_save signal handler that updates last_updated to the current time before saving.
Create a post_save signal handler that prints 'Book saved!' after saving.
Connect the signal handlers properly to the Book model.
💡 Why This Matters
🌍 Real World
Automatically updating timestamps and triggering actions on model changes is common in web apps for data integrity and notifications.
💼 Career
Understanding Django signals is important for backend developers to handle side effects and keep code clean and modular.
Progress0 / 4 steps
1
Create the Book model
Create a Django model called Book with a title field as models.CharField(max_length=100) and a last_updated field as models.DateTimeField(null=True, blank=True).
Django
Hint
Use models.CharField for the title and models.DateTimeField for last_updated with null=True and blank=True.
2
Create a pre_save signal handler
Import pre_save from django.db.models.signals and receiver from django.dispatch. Create a function called update_last_updated that takes sender, instance, and **kwargs. Inside it, set instance.last_updated to timezone.now(). Use @receiver(pre_save, sender=Book) to connect it.
Django
Hint
Use @receiver(pre_save, sender=Book) decorator and set instance.last_updated = timezone.now().
3
Create a post_save signal handler
Import post_save from django.db.models.signals. Create a function called notify_book_saved that takes sender, instance, created, and **kwargs. Inside it, print the string 'Book saved!'. Use @receiver(post_save, sender=Book) to connect it.
Django
Hint
Use @receiver(post_save, sender=Book) and print 'Book saved!' inside the function.
4
Connect signals in apps.py
In your app's apps.py, import AppConfig from django.apps. Create a class called LibraryConfig inheriting from AppConfig with name = 'library'. Override the ready() method to import the models module with import library.models.
Django
Hint
Override ready() in LibraryConfig to import library.models so signals connect on app start.
Practice
(1/5)
1. What is the main purpose of the pre_save signal in Django?
easy
A. To run code after a model instance is deleted
B. To run code before a model instance is saved to the database
C. To run code after a model instance is saved to the database
D. To validate form data before submission
Solution
Step 1: Understand the timing of pre_save
The pre_save signal triggers just before saving a model instance to the database.
Step 2: Compare with other signals
post_save runs after saving, and deletion signals run on delete, so they don't match pre_save.
Final Answer:
To run code before a model instance is saved to the database -> Option B
Quick Check:
pre_save = before save [OK]
Hint: Remember: pre_save runs before saving data [OK]
Common Mistakes:
Confusing pre_save with post_save
Thinking pre_save runs after saving
Mixing signals with form validation
2. Which of the following is the correct way to connect a post_save signal to a model named Book?
easy
A. post_save.connect(my_handler, sender=Book)
B. post_save.connect(Book, sender=my_handler)
C. Book.post_save.connect(my_handler)
D. connect.post_save(my_handler, Book)
Solution
Step 1: Recall the signal connection syntax
The correct syntax is signal.connect(handler_function, sender=ModelClass).
Step 2: Match the syntax to options
post_save.connect(my_handler, sender=Book) matches this pattern exactly, connecting my_handler to post_save for Book.
Final Answer:
post_save.connect(my_handler, sender=Book) -> Option A
Hint: Signal.connect(handler, sender=Model) is the pattern [OK]
Common Mistakes:
Swapping handler and sender arguments
Trying to call connect on the model
Using incorrect order of parameters
3. Given this code snippet, what will be printed when a new 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 save
medium
A. Updated: Alice
B. Created: Alice
Before saving: Alice
C. Before saving: Alice
Created: Alice
D. Before saving: Alice
Updated: Alice
Solution
Step 1: Understand signal order on save
pre_save runs before saving, so it prints 'Before saving: Alice' first.
Step 2: Check post_save behavior for new instance
post_save runs after saving; since created=True, it prints 'Created: Alice'.
Final Answer:
Before saving: Alice
Created: Alice -> Option C
Quick Check:
pre_save then post_save with created=True = Before saving: Alice
Created: Alice [OK]
Hint: pre_save prints first, post_save with created=True prints second [OK]
Common Mistakes:
Assuming post_save runs before pre_save
Ignoring the created flag in post_save
Mixing update and create messages
4. You wrote this signal handler but it runs whenever ANY model is saved (not specifically for 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)
medium
A. Missing sender argument in @receiver decorator
B. Signal handler must be named post_save_handler
C. post_save signal cannot be used with Product model
D. print statement should be inside a try-except block
Solution
Step 1: Check the @receiver decorator usage
The @receiver(post_save) decorator needs a sender=ModelClass argument to connect specifically to Product.
Step 2: Understand why handler runs for all
Without sender=Product, the handler listens to post_save for all models, causing it to run whenever any model is saved.
Final Answer:
Missing sender argument in @receiver decorator -> Option A
Quick Check:
@receiver(post_save, sender=Model) required [OK]
Hint: Always specify sender=Model in @receiver for signals [OK]
Common Mistakes:
Omitting sender argument in @receiver
Assuming handler name matters
Thinking post_save can't be used with certain models
5. You want to automatically set a model's slug field based on its title before saving, but only if the slug is empty. Which signal and approach is best?
hard
A. Use a form validation method to set slug before saving
B. Use post_save signal to update slug after saving the instance
C. Override the model's save() method and set slug after calling super().save()
D. Use pre_save signal to check if slug is empty and set it from title
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_save runs before saving, allowing modification of fields like slug before the database write.
Step 3: Why not post_save or save override
post_save runs after saving, so changing slug then requires another save. Overriding save() is possible but pre_save keeps logic separate and clean.
Final Answer:
Use pre_save signal to check if slug is empty and set it from title -> Option D
Quick Check:
Set fields before save with pre_save [OK]
Hint: Modify fields before saving with pre_save signal [OK]
Common Mistakes:
Setting slug after saving causing extra saves
Overriding save() but setting slug too late
Using form validation which may not cover all saves