The pre_save Signal
In this page:
What Runs Before a Save?
pre_save fires right before Django writes a model instance to the database, letting you inspect or adjust it first.
Example: What Runs Before a Save?
pre_save fires right before Django writes a model instance to the database, letting you inspect or adjust it first.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Article
@receiver(pre_save, sender=Article)
def before_article_save(sender, instance, **kwargs):
print(f"About to save: {instance.title}")
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
Auto-Normalizing a Field
A common use of pre_save is cleaning up a field's value automatically, such as lowercasing an email address before it's stored.
Example: Auto-Normalizing a Field
A common use of pre_save is cleaning up a field's value automatically, such as lowercasing an email address before it's stored.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Subscriber
@receiver(pre_save, sender=Subscriber)
def normalize_email(sender, instance, **kwargs):
instance.email = instance.email.strip().lower()
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
Detecting New vs Existing Objects
pre_save has no created flag because the row hasn't been written yet. Check instance._state.adding to tell whether this save will INSERT or UPDATE.
Example: Detecting New vs Existing Objects
pre_save has no created flag because the row hasn't been written yet. Check instance._state.adding to tell whether this save will INSERT or UPDATE.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Article
@receiver(pre_save, sender=Article)
def set_created_flag(sender, instance, **kwargs):
if instance._state.adding:
print("This is a brand new Article")
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
Keeping pre_save Fast
pre_save runs synchronously on every save() call, so avoid heavy database queries or external calls inside it — keep the logic simple and fast.
Example: Keeping pre_save Fast
pre_save runs synchronously on every save() call, so avoid heavy database queries or external calls inside it — keep the logic simple and fast.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Article
@receiver(pre_save, sender=Article)
def guard_title_length(sender, instance, **kwargs):
if len(instance.title) > 200:
instance.title = instance.title[:200]
{# Django-only code -- models.py/views.py/urls.py/settings.py
snippets, or template markup using Django template tags/variables
-- can't run standalone via Judge0 or the browser preview, since
it needs a real Django project. Only this course's pure-Python
examples (example_lang == 'python', no Django imports) are
actually runnable, so those still get the button below. #}
- Trying to read instance.pk in pre_save on a new object and assuming it already exists in the database.
- Doing expensive queries inside pre_save that slow down every single save() call.
- Confusing pre_save with a form's clean() method — pre_save runs on every save(), not just form submissions.
- pre_save fires just before a model instance is written to the database.
- It's commonly used to normalize or auto-generate field values before saving.
- Unlike post_save, there's no created flag — check instance._state.adding instead.
- Keep pre_save receivers fast since they run synchronously on every save().
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: