Signals

Django signals are a notification system that lets certain senders tell a set of receiver functions that an action has taken place — for example, running code automatically every time a model instance is saved or deleted — so you can decouple side effects from the code that triggers them.

Learn Signals in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll meet the built-in model signals, learn how to connect receivers with the @receiver decorator, build the classic "auto-create a profile when a user is created" pattern, and register everything correctly so it actually fires.

What You'll Learn in This Lesson

1 Built-in Model Signals

Every Django model fires signals at key moments in its lifecycle. The most important ones live in django.db.models.signals and fire around saving and deleting a row:

A receiver function always accepts the same keyword arguments. Django passes sender (the model class), instance (the actual object), and for save signals a created boolean. You should always end the signature with **kwargs so future arguments don't break your code.

The example below builds a tiny signal dispatcher in plain Python so you can watch post_save fire without a database. It mirrors how Django calls each connected receiver.

2 Connecting Receivers with @receiver

A receiver is just a function. To make it listen for a signal you connect it. The cleanest way is the @receiver decorator, which takes the signal and a sender to filter on:

You can also connect receivers manually with .connect() — useful when the receiver is defined elsewhere or connected conditionally:

The example below recreates that "auto-create a profile" flow with the same mini dispatcher, so you can see the created guard doing its job.

3 Registering Signals & Custom Signals

Receivers only register when their module is imported and the @receiver decorators actually run. The documented place to make that happen is the ready() method of your app's config in apps.py :

Beyond the built-ins, you can define your own custom signals for events that matter to your app. Create one with Signal() and fire it with .send() :

The runnable example wires up a custom signal end to end — define it, connect a receiver, and send it.

🎯 Your Turn: Connect the Receiver

Fill in the blank so the receiver listens for the signal that fires right after a row is saved. The function should only print on creation.

Common Errors (And How to Fix Them)

The receiver module is never imported, so the @receiver decorators never run and nothing is connected.

✅ Fix: import your signals module from the ready() method of your app's AppConfig in apps.py .

❌ A duplicate profile is created on every update

The receiver runs on every save because the created flag isn't being checked.

✅ Fix: wrap the side effect in if created: so it only runs on the first save.

❌ RecursionError: maximum recursion depth exceeded

A post_save receiver calls .save() on the same instance, which fires post_save again, looping forever.

✅ Fix: update fields in a pre_save receiver instead, or use queryset.update() which does not re-fire the signal.

📋 Quick Reference

Signal / Tool

When It Fires / What It Does

pre_save

Just before an instance is written to the database

post_save

Right after a save; provides the created flag

pre_delete

Just before an instance is deleted

post_delete

Right after an instance is deleted

@receiver

Decorator that connects a function to a signal and sender

🏆 Mini Challenge: Audit Log Receiver

Build a small audit log. Connect receivers to both a save and a delete signal, then fire each one and watch the log fill up.

❓ Frequently Asked Questions

Lesson 21 complete — you can wire up signals!

You now know the built-in model signals, the sender/instance/created arguments, how to connect receivers with @receiver and .connect() , where to register them in ready() , and how to define your own custom signals.

🚀 Up next: Middleware — learn how to hook into every request and response as it passes through Django.

Practice quiz

What are Django signals used for?

  • Running code automatically when an event like a save occurs
  • Defining URL routes
  • Validating forms
  • Creating database indexes

Answer: Running code automatically when an event like a save occurs. Signals let receivers run code automatically when senders report an action, e.g. a save.

When does the pre_save signal fire?

  • After the row is written
  • Just before an instance is written to the database
  • Only on delete
  • When the server starts

Answer: Just before an instance is written to the database. pre_save fires just before the instance is saved; on a first save it has no primary key yet.

Which signal provides a 'created' boolean argument?

  • pre_save
  • pre_delete
  • post_save
  • post_delete

Answer: post_save. post_save fires after the save and passes created=True/False to the receiver.

Which decorator connects a function as a signal receiver?

  • @signal
  • @connect
  • @listener
  • @receiver

Answer: @receiver. The @receiver(signal, sender=...) decorator registers a function as a receiver.

Why should a receiver function end its signature with **kwargs?

  • To rename arguments
  • To stay future-proof against new signal arguments
  • To make it run faster
  • To skip the sender

Answer: To stay future-proof against new signal arguments. Ending with **kwargs keeps the receiver working if Django adds new keyword arguments.

In the auto-create-profile pattern, why guard with 'if created'?

  • So a profile is created only on the first save, not every update
  • To speed up queries
  • To avoid importing models
  • To delete old profiles

Answer: So a profile is created only on the first save, not every update. Without the created guard, the receiver would try to create a profile on every save.

Where is the documented place to import your signals module so receivers register?

  • In settings.py
  • In urls.py
  • In the AppConfig.ready() method in apps.py
  • In manage.py

Answer: In the AppConfig.ready() method in apps.py. Importing the signals module from AppConfig.ready() reliably registers the receivers.

Which method fires a signal to all connected receivers?

  • signal.fire()
  • signal.emit()
  • signal.trigger()
  • signal.send()

Answer: signal.send(). You dispatch a signal with signal.send(sender=..., **kwargs).

What argument does Django pass as the actual saved object?

  • instance
  • sender
  • model
  • obj

Answer: instance. instance is the actual object; sender is the model class.

How can you connect a receiver without the @receiver decorator?

  • signal.add(func)
  • signal.connect(func, sender=...)
  • signal.register(func)
  • signal.bind(func)

Answer: signal.connect(func, sender=...). signal.connect(receiver, sender=...) connects a receiver manually.

Continue this course