|Version 38 (modified by 7 years ago) (diff),|
- Read the current Signal Documentation
- Signals have been refactored in a backwards-incompatible way. See BackwardsIncompatibleChanges for more details.
What Are Signals?
Django includes an internal "dispatcher" which provides two pieces of functionality:
- Pieces of code which want to advertise what they're doing can use the dispatcher to send "signals" which contain information about what's happening.
- Pieces of code which want to do something whenever a certain event happens can use the dispatcher to "listen" for particular signals, then execute the functions they want when those signals are sent out.
The actual mechanism comes from a third-party library called PyDispatcher, which is bundled with Django and which lives at
django.dispatch.dispatcher. To improve performance the API and implementation has been refactored, as described at the end of this document.
How signals work
A signal is an instance of django.dispatch.Signal acting as reference point for a single activity, and requires nothing special to set up. For example, the
post_save signal, found in
django.db.models.signals, is defined like so:
post_save = django.dispatch.Signal()
When a piece of code wants to send a signal, it needs to do two things:
- Import the signal from wherever it's been defined.
- Use the signal's
sendfunction to send the signal.
An example of this can be found in the
save method of the base model class,
django.db.models.Model; the file in which that class is defined imports the signals defined in the
from django.db.models import signals
And in the very last line of the
save method, it sends the
The first argument is fairly clear, and is common for all uses of the dispatcher;
sender is the object which is sending the signal; in this case,
self.__class__; this will be whatever model class the object which was just saved belongs to.
The second argument,
instance, is one of the more interesting features of the dispatcher: when sending a signal, you are free to include any extra arguments beyond the first; if a function which is listening for the signal is expecting certain information to be passed in as arguments, the dispatcher will ensure that the extra arguments to
send are used. In this case the extra argument
self, which means it will be the object which was just saved. This means that functions which are listening for the
post_save signal can, if they want to do something with the object that was just saved, specify that they take an argument named
To listen for a signal, first define a function that you want to execute when the signal is sent; if you know that the signal will be sent with extra arguments and you want to use those arguments, make sure your function accepts them. Then you need to do three things:
- Make sure there is a reference to your signal handler somewhere. If it is defined as a local function, chances are it will be garbage collected and won't receive any signals.
- Make sure that the signal handler has kwargs as the last parameter.
- Import the signal object you'll be listening for.
- Use the signal's
connectmethod to tell the dispatcher you want to listen for something.
A good example of this is found in Django's bundled "contenttypes" application, which creates and maintains a registry of all the installed models in your database. In order to do this, the contenttypes app defines a
ContentType model, and it needs to know any time a new model is installed so it can create the appropriate
ContentType object for that model. To do this, it includes a file called
manage.py syncdb is run, it loops through every application in the
INSTALLED_APPS setting, and looks to see if any apps contain a module called
management; if they do,
manage.py imports them before installing any models, which means that any dispatcher connections listed in an app's
management module will be set up before model installation happens.
management.py file, the contenttypes app defines a function called
update_contenttypes, which takes three arguments:
verbosity. These correspond to the extra arguments
manage.py will use with
dispatcher.send when it sends the
post_syncdb signal after installing each new application, and provide enough information to determine which models need to have new
ContentType objects created (actually, just
created_models would be enough; the extra argument,
verbosity, is used by
manage.py to indicate whether any listening functions should be "verbose" and echo output to the console, or be quiet and not echo any output.
management.py file also imports
django.dispatch.dispatcher; after the
update_contenttypes function is defined, it sets up that function to listen for the
The method is called on an instance of django.dispatch.Signal, identifying the signal to listen for. The first argument,
update_contenttypes, is a reference to the function to execute when the signal is sent out. There is another optional argument,
sender, which is not used in this example;
sender can be used to narrow down exactly what will be listened for; when you specify
sender, your function will only be executed when the object which sent the signal is the same as the object you specify as the
An example of this can be found in Django's authentication application:
django.contrib.auth.management defines a function called
create_superuser, and uses the dispatcher to connect to the
post_syncdb signal -- but only when
post_syncdb is being sent as a result of installing the auth application. To do this, the auth app's
management.py file imports its own models:
from django.contrib.auth import models as auth_app
And then uses the
sender argument to
Here's a breakdown of exactly why that works:
manage.py syncdbfinishes installing the models for a particular application, it sends the
post_syncdbsignal. You'll remember that
dispatcher.sendtakes an optional argument,
sender, which is the object that's "sending" the signal. In this case,
senderto be the
modelsmodule of the app it just installed.
django.contrib.auth.managementimport the auth app's models as
auth_app, which means that, within that file, the variable
auth_appis the module
- So when
manage.py syncdbsend the
senderargument, the dispatcher notices that this is the same as the
senderspecified in the
django.contrib.auth.management, and so the
create_superuserfunction is executed.
In case you've ever wondered, that's how Django knows to prompt you to create a superuser whenever you install the auth app for the first time. The auth app also sets up another function --
create_permissions -- which doesn't specify
sender in its call to
dispatcher.connect, so it runs any time
post_syncdb is sent. That's how the auth app creates the add, change and delete
Permission objects for each application you install.
List of signals built in to Django
Django defines several sets of signals which are used internally, and which you can listen for in order to run your own custom code at specific moments.
 refactored signals and
django.dispatch with an eye towards speed. The net result was up to a 90% improvement in the speed of signal handling, but along the way some backwards-incompatible changes were made:
- All handlers now must be declared as accepting
- Signals are now instances of
django.dispatch.Signalinstead of anonymous objects.
- Connecting, disconnecting, and sending signals are done via methods on the
Signalobject instead of through module methods in
django.dispatch.dispatcher. The module-level methods are deprecated.
Anysender options no longer exist. You can still receive signals sent by any sender by using
So, a quick summary of the code changes you'd need to make:
| || |
| || |
| || |
| || |
| || |
Tips and Troubleshooting
Help, post_save seems to be emitted twice for each save!
The short of it is that the signal is bound twice due to double-importing of whatever module is doing the binding. The workaround is to pass
dispatch_uid="some.unique.identifier" when binding signals:
signals.post_save.connect(my_handler, MyModel, dispatch_uid="path.to.this.module")
The dispatch_uid string can be any unique string. Since the goal is to prevent connect() from being called twice due to its parent module being imported twice, a good value to use for dispatch_uid is the module's name or path. However, dispatch_uid can be any unique identifier. The net effect is that
signals.signal_name.connect will only bind the signal once for each dispatch_uid, even if
connect is called multiple times.
- Initializing application data in Django -- blog entry by Alan Green which discusses use of the
post_syncdbsignal to provide initial application data.
- Django tips: laying out an application -- blog entry by James Bennett which mentions the use of the
post_syncdbsignal to execute custom functions when an application is installed.
- Django signals -- Mercurytide whitepaper explaining Django's dispatcher.
- Signals in Django: Stuff That’s Not Documented (Well) -- blog entry by Chris Pratt which mentions the use of the signals when creating/updating objects and also how to handle signals asynchronously.