← Back to Django Course | Chapter 12: Signals & Middleware | Lesson 3 of 8

The pre_save Signal

pre_save rings the bell right before something is saved, so you can adjust it at the last second.

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.

markup
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.

markup
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.

markup
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.

markup
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. #}
Common Mistakes
  1. Trying to read instance.pk in pre_save on a new object and assuming it already exists in the database.
  2. Doing expensive queries inside pre_save that slow down every single save() call.
  3. Confusing pre_save with a form's clean() method — pre_save runs on every save(), not just form submissions.
Chapter Summary
  • 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().

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.