The pony I really want: a good signals tutorial

Sep 16, 2008 Django

Someone needs to write a good, clear, thorough tutorial on using signals in Django. They're either a highly powerful but underutilized tool, or a cool but completely useless feature - I haven't been able to figure out which yet. I mean, I get the concept, but what I really need are some use cases to make my understanding complete. For now the work project I'm working on is my only real-world example, so here's my contribution to the pool. Signals are, for all intents and purposes, messages sent out from the framework that relay information that can be used by all of the apps in a project. To make use of the information sent in these signals, you just have to define a method to "catch" it. There are a few different classes of predefined signals: model, management, request/response, and test. I'm not sure under what circumstance management signals would be useful other than for testing/deployment. Request/response signals (imported from django.core.signals) don't seem to relay a lot of useful information either. For example, a callback method like this:

def my_callback(sender, **kwargs):
    print sender
    print kwargs['signal'].__dict__

from django.core.signals import request_finished
request_finished.connect(my_callback)
Returns stuff like this:
<class 'django.core.handlers.wsgi.WSGIHandler'>
{'providing_args': set([]), 'receivers': [((5729776, 2022048), <weakref at 0x5e2a20; to 'function' at 0x576df0
(close_connection)>), ((21497136, 2022048), <weakref at 0x14af210; to 'function' at 0x1480530 (my_callback)>),
((22355760, 2022048), <weakref at 0x155f6f0; to 'function' at 0x1551f30 (my_callback)>)]}
So, there's not a lot of information in this particular signal that you can use to change the user flow or set conditions or trigger some kind of action. The model signals show a lot of promise though. Stick something like this at the top of one of your models.py files - any model will do, as long as it's in a place where the code will be executed before the signal is sent:
def my_callback(sender, instance, **kwargs):
    print sender_
    print instance

from django.db.models.signals import post_init
post_init.connect(my_callback)
Depending on how complex your project is, you might be surprised to see how many instances of your models are being created on each page request:
<class 'django.contrib.sessions.models.Session'>
Session object
<class 'django.contrib.sessions.models.Session'>
Session object
<class 'django.contrib.sites.models.Site'>
127.0.0.1:8000
<class 'django.contrib.sites.models.Site'>
127.0.0.1:8000
<class 'category.models.Vertical'>
IT Management
<class 'category.models.Vertical'>
IT Management
Note: Take a close look at the complete signal reference as you're writing your receiver methods - each signal sends a slightly different set of arguments. Put some logic in your listener method:
def my_callback(sender, args, **kwargs):
    print sender
    if sender is User: print 'Yeehaw!'

from django.db.models.signals import pre_init
pre_init.connect(my_callback)
Or specify a sender:
def my_callback(sender, args, **kwargs):
    print sender
    print 'Yeehaw!'

from django.db.models.signals import pre_init
pre_init.connect(my_callback, sender=User)
Both will give you a whole lot of this:
<class 'myproject.myaqpp.models.User'>
Yeehaw!
<class 'myproject.myaqpp.models.User'>
Yeehaw!
<class 'myproject.myaqpp.models.User'>
Yeehaw!
<class 'myproject.myaqpp.models.User'>
Yeehaw!
Uhm, no, this is definitely not all I have to say on the matter of signals. I've been tinkering around today, but I do actually have a use case - in the work project, we're thinking of using signals to handle user redirects when a user signs up/logs in after having created some content. But the exact flow hasn't been worked out yet (oh yeah, we're working without a spec). Once it has, I'm looking forward to writing a custom signal - that'll probably be tomorrow. Meantime, check out this excellent article from Chris Pratt: Signals in Django: Stuff That's Not Documented (Well) [February 16th, 2008]