Chapter 16 · Caching, files, mail, signals and tasks
16.2 The signals Django sends
Every Signal Django defines, the code that sends it, its sender and its keyword arguments: the request signals, the model signals such as pre_save and m2m_changed, post_migrate, setting_changed, connection_created, and the login, autoreloader and task signals.
Django’s signals are module-level objects: the request signals and setting_changed in django/; the model signals, class_prepared, pre_migrate and post_migrate in django/; connection_created in django/; template_rendered in django/; the login signals in django/; the autoreloader’s in django/; the tasks’ in django/. Django listens to several itself: it closes connections and caches on the request signals, django registers PostgreSQL’s types on connection_created (Opening and closing a connection), and makes the contrib applications’ rows on post_migrate.
Where each signal is sent
"signal" and "sender" (Signal.send). The login signals belong to The user of a request: login, logout and AuthenticationMiddleware and Authenticating: authenticate and the backends, the autoreloader’s to The autoreloader, the tasks’ to Background tasks: Task, TaskResult and the task backends.| signal | sent by | sender | keyword arguments |
|---|---|---|---|
request_started |
sent by a handler (WSGIHandler django/ASGIHandler django/ |
sender the handler’s class | keyword arguments "environ", or "scope" under ASGI |
request_finished |
sent by closing the response (Http django/ASGIHandler when there is no response |
sender the handler’s class; None under the test client |
keyword arguments none |
got_ |
sent by an exception becoming a 500 (response_ django/get_) |
sender None |
keyword arguments "request" |
setting_changed |
sent by override_ django/override_; a Transaction with available_apps |
sender the settings object’s class | keyword arguments "setting", "value", "enter" |
class_prepared |
sent by Model django/ |
sender the model class | keyword arguments none |
pre_init |
sent by Model.__init__ django/ |
sender the model class | keyword arguments "args", "kwargs" |
post_init |
sent by Model.__init__ |
sender the model class | keyword arguments "instance" |
pre_save |
sent by Model.save_base django/ |
sender the model class | keyword arguments "instance", "raw", "using", "update_fields" |
post_save |
sent by Model.save_base |
sender the model class | keyword arguments "instance", "created", "update_fields", "raw", "using" |
pre_delete |
sent by Collector.delete django/ |
sender the model class | keyword arguments "instance", "using", "origin" |
post_delete |
sent by Collector.delete |
sender the model class | keyword arguments "instance", "using", "origin" |
m2m_changed |
sent by a many-to-many manager (create_ django/ |
sender the through model | keyword arguments "action", "instance", "reverse", "model", "pk_set", "using", "raw" |
pre_migrate |
sent by emit_ django/ |
sender an AppConfig |
keyword arguments "app_config", "verbosity", "interactive", "using"; from migrate, "stdout", "apps", "plan" |
post_migrate |
sent by emit_ django/ |
sender an AppConfig |
keyword arguments as pre_migrate |
connection_ |
sent by Base django/ |
sender the wrapper’s class | keyword arguments "connection" |
template_ |
sent by instrumented_ django/ |
sender the template | keyword arguments "template", "context" |
user_logged_in |
sent by login django/alogin |
sender the user’s class | keyword arguments "request", "user" |
user_ |
sent by authenticate django/aauthenticate |
sender "django |
keyword arguments "credentials", secrets masked; "request" |
user_logged_out |
sent by logout django/alogout, before the session is flushed |
sender the user’s class, or the class of None |
keyword arguments "request", "user" |
autoreload_ |
sent by BaseReloader.run django/ |
sender the reloader | keyword arguments none |
file_changed |
sent by Base django/ |
sender the reloader | keyword arguments "file_path" |
task_enqueued |
sent by Immediate django/Dummy django/ |
sender the backend’s class | keyword arguments "task_result" |
task_started |
sent by Immediate |
sender the backend’s class | keyword arguments "task_result" |
task_finished |
sent by Immediate |
sender the backend’s class | keyword arguments "task_result" |
The request cycle and the test framework
The request signals and the receivers Django connects to them are in the table of The two entry points.
Every send of setting_changed is in the test framework. override_settings sends it for each setting as an override begins, with "enter" true, and again as it ends, by Signal, with the restored value; a TransactionTestCase with available_apps sends it for "INSTALLED_APPS". Most of its receivers throw away what was built from the old value (Overriding settings). template_rendered is sent only once setup_test_environment has made instrumented_test_render the templates’ render method.
A model class and its instances
class_prepared is sent at the end of ModelBase, when the class is complete and not yet registered (ModelBase: a class statement becomes a model). It is sent for every model that is not abstract, migrations’ historical models included: rendering the harbour’s project state sent it once for each model.
Model.__init__ sends pre_init before it sets anything and post_init at its end, each time it runs, whether or not anything listens, and so for each row a query fetches, which Model.from_db makes by calling the class with the row’s values as "args" (An instance and its state). from_db sets ModelState.db and ModelState only after the class returns, so to a receiver of post_init a fetched instance looks new and from no database. An unpickled instance, one read from a cache among them, is made without calling the class and sends neither.
Saving: pre_save and post_save
Model.save_base sends pre_save before it writes any table and post_save after the last, except for a through model Django made (Saving an instance). QuerySet.update, QuerySet and QuerySet send neither.
Django passes "raw" true only from DeserializedObject, which calls save_base itself, past the model’s save, so that a fixture’s rows are written as they stand; since 6.1 it passes "raw" to m2m_changed too (Serialization: serialize, deserialize and DeserializedObject). Django’s reference warns such a receiver not to query or change other rows, since the database may not be consistent yet.
"update_fields" is None unless the save was limited, and then a frozenset: of the names the caller gave, or, for an instance with deferred fields, of attnames Model.save picks itself. Given an empty list, save returns before either signal.
The sender is always the instance’s own class, a proxy’s or a child’s. Saving an Officer, a child of Sailor with a table of its own, sent both signals for Officer alone, since save_base writes a parent’s row without a signal; deleting it sent each delete signal for Officer and again for Sailor, whose row the collector holds as an instance of Sailor.
m2m_changed
A many-to-many manager sends m2m_changed in pairs around each change, with an "action" of "pre_add", "post_add", "pre_remove", "post_remove", "pre_clear" or "post_clear"; set sends those of the changes it makes (Many-to-many relations). "pk_set" is, for an addition, the keys that had no row yet, looked up by a query; for a removal, the keys the caller named, whether a row had them or not; for a clearing, None.
Deleting: pre_delete and post_delete
Collector.delete sends pre_delete for every instance it holds before any row goes, and post_delete after each model’s rows, inside its transaction; "origin" is the instance or queryset whose delete was called (Deleting: the Collector). Rows it deletes without holding them are not announced, and a receiver connected for a model, or for every sender, makes Collector refuse to delete that model’s rows unseen. Nor are rows of a through model Django made, or rows the database deletes for DB_CASCADE.
pre_migrate and post_migrate
Each is sent once for every application in the registry that has a models module. The migrate command sends both around the executor and adds "stdout", "plan" and "apps", a registry of historical models (The migrate command, showmigrations and sqlmigrate). post_migrate has two more senders that add none of them: the flush command, after emptying a database that had tables unless its caller inhibits it, and a TransactionTestCase with available_apps, for the applications it keeps. A receiver therefore needs a default for "apps", as create_permissions has.