signals.py#

What does “Register signals” mean in Django?#

Registering signals means connecting Django events (like save or delete) to custom functions that run automatically when those events occur.

First: What is a signal?#

A signal is Django’s way of saying:

“Something happened — do you want to react to it?”

Examples:
  • A model was saved

  • A model was deleted

  • A user logged in

  • A request started or finished

Django emits a signal, and your code can listen and respond.


Simple real-world analogy

Think of a doorbell:
  • Someone presses the button → 🔔 (signal)

  • The bell rings

  • You hear it and respond

In Django:
  • Model saved → 🔔 signal sent

  • Your function hears it

  • Your function runs automatically


Common Django signals

Some very common ones:

Signal

When it fires

post_save

After a model is saved

pre_save

Before a model is saved

post_delete

After a model is deleted

user_logged_in

When a user logs in

request_started

When request starts


What is registering a signal#

Registering a signal = telling Django:

“When THIS signal happens, run THIS function.”

Without registration:
  • Django doesn’t know your function exists

  • Your signal code never runs


Basic signal example

1️⃣ Create a signal handler

Create a file:

myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel


@receiver(post_save, sender=MyModel)
def mymodel_created(sender, instance, created, **kwargs):
    if created:
        print("A new MyModel was created!")
This function:
  • Listens to post_save

  • Runs after MyModel is saved

  • Only runs when it’s newly created


2️⃣ Register the signal (VERY IMPORTANT)

Django does not auto-load signals.py.

You must register it manually.

In apps.py:

from django.apps import AppConfig

class MyappConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        import myapp.signals

This line registers the signals:

import myapp.signals

Why register signals inside ready()?#

Because:
  • Django loads apps lazily

  • Models may not be ready at import time

  • ready() runs after everything is loaded

This is the safest place to register signals.


What happens if you don’t register signals?#

Signal file exists

Code is correct

Django never imports it

➡️ Signal never fires

This is one of the most common Django mistakes.


Mental model

Think like this:
  • signals.py = Listeners

  • ready() = Plugging the listeners in

  • No ready() = No sound 🔕


Why not import signals in models.py?#

Bad idea

It can cause:
  • Circular imports

  • App loading issues

  • Hard-to-debug bugs

Best practice:

Always register signals in apps.py → ready()


When should you use signals?#

Good use cases:
  • Auto-create related objects (profile for user)

  • Logging activity

  • Sending notifications

  • Keeping models in sync

Bad use cases:
  • Core business logic

  • Complex workflows

  • Things that must be explicit


Where to put signals in Django#

Short answer (best practice)

Put signals in:

myapp/signals.py

Register them in:

myapp/apps.py

This is the official, clean, production-safe Django pattern.


Recommended app structure

myapp/
├── __init__.py
├── admin.py
├── apps.py        <-- register signals here
├── models.py
├── signals.py     <-- write signals here
├── tests.py
├── views.py
├── migrations/

Why NOT put signals in other files?#

Let’s go through common mistakes

models.py (DON’T)

# BAD PRACTICE
from django.db.models.signals import post_save
Why bad?
  • Circular imports

  • Models may not be ready

  • Hard-to-debug startup bugs


views.py (WRONG)

Signals are:
  • Model-level events

  • System-level reactions

Views are:
  • HTTP request/response logic

Mixing these breaks separation of concerns.


__init__.py (NO)

This file should stay minimal.

Signals here can cause:
  • App loading order issues

  • Hidden side effects


Correct implementation (step by step)

1️⃣ Create signals.py

# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel


@receiver(post_save, sender=MyModel)
def mymodel_created(sender, instance, created, **kwargs):
    if created:
        print("MyModel created")

2️⃣ Register signals in apps.py

# myapp/apps.py
from django.apps import AppConfig


class MyappConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        import myapp.signals

This line activates your signals:

import myapp.signals

3️⃣ Ensure app config is used (IMPORTANT)

In settings.py:

INSTALLED_APPS = [
    'myapp.apps.MyappConfig',
]

This ensures ready() runs

‘myapp’ alone may skip signal registration in some setups


Django does not auto-load signals.py.

You must:
  1. Import it

  2. At the right time

  3. After apps + models are ready

That’s exactly what apps.py → ready() does.

Write signals in signals.py, register them in apps.py, never in models.py.


Simple mental model

File

Role

signals.py

What should happen

apps.py

When to activate

ready()

Plug everything in