One fea­ture of django is worth to note is that it sup­ports sig­nals. In fact, under its skin it imple­ments PyDis­patcher, a python library that allows to emit sig­nals and to dis­patch them.

If at first glance this couldn’t look so useful to you, well, this hasn’t been true for me since it was exactly that kind of stuff I was look­ing for. What I was trying to do was to imple­ment some kind of auto-​installer for an appli­ca­tion that I’m writ­ing, and in order to do so I had to run the set up after that the syncdb com­mand is issued. So the main prob­lem was: how do I know when a user does the syncdb in a non-​intrusive way?

After some googling, I found that in django exists the semi-​hidden fea­ture of sig­nals, so I began explor­ing them. The only thing you can look at on the offi­cial site is a page on their wiki, and addi­tion­ally there are some cool arti­cles over the net. Anyway, if you want to know a fast way to catch the syncdb com­mand just follow up the reading.

First of all, let say that django looks for a file named man​age​ment.py in every installed application’s direc­tory when­ever the syncdb com­mand is ran. In this file we’ll write our man­age­ment func­tions and we’ll link those func­tions to django through its signal system.

To do so we need to import some basic django’s modules:

from django.dispatch import dispatcher
from django.db.models import signals

With these mod­ules we have every­thing to handle the django’s sig­nals, and so here we go:

dispatcher.connect(do_something_with_the_signal, signal=signals.post_syncdb, sender=model)

With that func­tion we con­nect our func­tion do_something_with_the_signal to the django’s signal han­dler. A word apart has to be given to the sender attribute. In that way we say that our func­tion must be called only when the appro­pri­ate model gets called in the syncdb phase.

The signal handling’s func­tion must be defined as following:

def do_something_with_the_signal(app, created_models, verbosity, **kwargs):

So let describe the para­me­ter list:

  • app: the dispatcher sender’s attribute
  • created_models: a list of models that have been created with this syncdb
  • verbosity: django’s verbosity level
  • kwargs: a variable-length’s list of optional arguments

Let sup­pose we want to create a func­tion that inserts the settings.LANGUAGES list into a model (as I had to do for a little work). Here is how the code looks like:

from django.conf import settings
from django.dispatch import dispatcher
from django.db.models import signals
from project.translatable import models as transmodel

def create_language_list(app, created_models, verbosity, **kwargs):
    for lang in settings.LANGUAGES:
        lcode, literal = lang

        if transmodel.Language in created_models:
            print "Adding %s (%s)..." % (literal, lcode)
            new_lang = transmodel.Language(code=lcode, name=literal)
            new_lang.save()

dispatcher.connect(create_language_list, signal=signals.post_syncdb, sender=transmodel)

It’s worth noting here that using the print state­ment is per­fectly OK (this is done by django’s auth system for exam­ple). Another thing to note is that we check that our model has been cre­ated in that syncdb’s phase. I do so because in this way the model is filled just the first time the model is created.

Another use of this stuff is to insert ini­tial raw data into the model by using django’s ORM and not by using raw SQL. In this way we’ll be database-​backend indipen­dent and we’ll have a trans­par­ent way to have ini­tial data in our models.