Signals¶
next.dj emits Django signals from every subsystem. A signal is the right integration point when external code needs to react to a framework event without subclassing or monkey patching. This page lists every signal, its payload, and the typical patterns for receiver functions.
Import Surface¶
Every signal lives in the subpackage that emits it.
The aggregator next.signals re-exports every name so handlers can pull from a single import.
# Aggregator
from next.signals import action_dispatched, page_rendered
# Subpackage import
from next.forms.signals import action_dispatched
from next.pages.signals import page_rendered
Both styles are valid. Use the aggregator when a single module subscribes to events from several subsystems.
Catalog¶
Every signal the framework emits is listed below with the subsystem that
emits it and the moment it fires. Signals Reference holds the
canonical payload table, the sender value and the keyword arguments for
each signal.
Signal |
Subsystem |
When it fires |
|---|---|---|
|
Pages |
After a template source is registered on a page. |
|
Pages |
After a context callable is attached to a page module. |
|
Pages |
After the page renders to HTML and the static assets are injected. |
|
Components |
After a single component is registered. |
|
Components |
After a batch of components is registered. |
|
Components |
After a component backend is created from its configuration entry. |
|
Components |
After a component is rendered to HTML. |
|
Dependencies |
When a |
|
URLs |
After a URL pattern is created for a discovered page. |
|
URLs |
After the router manager rebuilds its pattern set. |
|
Forms |
After the backend stores a handler for an action name. |
|
Forms |
After an action handler runs and the response is coerced. |
|
Forms |
When a bound form fails validation during dispatch. |
|
Static |
After a file is registered with a backend and added to the collector. |
|
Static |
When the static manager begins injection, after rendering completes. |
|
Static |
After placeholder replacement completes. |
|
Static |
After the static factory instantiates a backend. |
|
Server |
After the reloader resolves the full list of watch specs. |
|
Configuration |
After the settings layer drops its caches. |
The forms and static signals have dedicated topic pages with worked receiver examples: Form Signals and Static Signals.
Receiver Patterns¶
Connect once at startup.
Django’s app registry is not fully initialised at module import time, so the receiver import lives inside ready.
This is the one approved exception to the module-level import rule.
from django.apps import AppConfig
class NotesConfig(AppConfig):
name = "notes"
def ready(self) -> None:
from notes import receivers # noqa: F401, PLC0415
Use django.dispatch.receiver to connect a callable to a signal.
import logging
from django.dispatch import receiver
from next.signals import action_dispatched
logger = logging.getLogger(__name__)
@receiver(action_dispatched)
def log_dispatch(sender, **kwargs) -> None:
logger.info("action dispatched: %s", kwargs["action_name"])
Multiple Receivers¶
Several receivers can connect to the same signal. They run in registration order.
Disconnecting¶
Call signal.disconnect(receiver) when a receiver should stop firing.
The same dispatch_uid passed to connect can be supplied to disconnect so a receiver wired by string identifier can also be removed by that identifier.
from next.signals import action_dispatched
def log_dispatch(sender, **kwargs) -> None:
...
action_dispatched.connect(log_dispatch, dispatch_uid="notes.log_dispatch")
action_dispatched.disconnect(dispatch_uid="notes.log_dispatch")
The SignalRecorder from next.testing.signals disconnects its receivers on context-manager exit, or when stop() is called explicitly.
Test Helpers¶
The SignalRecorder from next.testing.signals captures events for assertions.
Each captured event is a SignalEvent with signal, sender, and kwargs attributes.
from next.signals import action_dispatched
from next.testing.client import NextClient
from next.testing.signals import SignalRecorder
def test_emits_action(db) -> None:
with SignalRecorder(action_dispatched) as recorder:
NextClient().post_action("create_note", {"title": "Hello"})
assert len(recorder.events) == 1
assert recorder.first_for(action_dispatched).kwargs["action_name"] == "create_note"
SignalRecorder also provides events_for(signal), first_for(signal), and last_for(signal) for targeted access.
See Testing for the full testing surface.
Common Patterns¶
Observe Framework Signals walks through the audit trail, cache invalidation, hot reload, and observability patterns with production-sized receiver code.
See Also¶
See also
Form Signals for the forms-specific signals.
Static Signals for the static-specific signals.
Testing for SignalRecorder and other helpers.
Observe Framework Signals for production sized receivers.
Signals Reference for the public API.