Signal vs overriding save
I'm a big fan of signals and wonder why people are so adverse to using them, preferring overriding save instead.
Probably every day on the Django IRC channel, someone asks about how to set a user on a model or about setting the time automatically. Fortunately thanks to this excellent article, we are all familiar with the former. The latter is easy to do as well, without having to resort to using the now deprecated (maybe) auto_now and auto_now_add.
Here's how to do it overriding the save:
from django.db import models
from datetime import datetime
class Todo(models.Model):
text = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField()
def save(self, *args, **kw):
if not self.id:
self.timestamp = datetime.now()
super(Todo, self).save(*args, **kw)
And here's how to do it with a signal:
from django.db import models
from datetime import datetime
class Todo(models.Model):
text = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField()
def add_date(sender, instance, **kw):
instance.timestamp = datetime.now()
models.signals.pre_save.connect(add_date, sender=Todo)
Setting up a signal is surprisingly easy and one line less of code. Is it more expensive from a CPU point of view? Not really. Here's a script that ran the two models:
from chapter_2.recipe_3.models import Todo
from time import time
start = time()
for x in range(0, 10000):
todo = Todo()
todo.text = "Get some milk %s" % x
todo.save()
diff = time() - start
print "Time: %s" % diff
Running this with signals:
Time: 6.97824287415
Running this with save:
Time: 6.53473687172
I re-ran it a few times and never got a significant difference between the two. Sometimes the signals were faster. I think I can be reasonably sure that signals (in this simple scenario) do not add significant performance cost. So what's the problem with signals?
The advantages:
- The signal can be applied to any number of models, without having to repeat override save all over the place. Admittedly in the above example, if you made a base class and override save there, you wouldn't have the problem.
- Can be applied to models you don't necessarily have access to or want to modify.
- Overriding methods make me uncomfortable. The save is pretty obvious, but it's easy to end up with a model that has a bunch of magic methods. Renaming one of them will cause grief as you are no longer overriding the correct one. And once I forgot to call super in the save, but that's just my own incompetence.
The disadvantage:
- You can end up with a lot of signals and forget what signal occurs when (easier to lose track than lots of save's in the model inheritance)
Since creating a new signal and assigning to just one specific model is very easy, the argument "use save when its specific to the model" seems moot. It's just as easy to make a signal as the save.
Where to put signals?
- If it's a signal related solely to one model, I recommend on the model
- If it's a signal that is used on multiple places, I recommend a signals.py module (just make sure to add it to __init__.py so that's imported)
Further reading
- http://stackoverflow.com/questions/170337/django-signals-vs-overriding-save-method
- http://www.martin-geber.com/thought/2007/10/29/django-signals-vs-custom-save-method/ (old)
Comments
There are no comments.
Login to add comments

