Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - how do I _not_ dispatch a signal?

I wrote some smart generic counters and managers for my models (to avoid select count queries etc.). Therefore I got some heavy logic going on for post_save.

I would like to prevent handling the signal when there's no need to. I guess the perfect interface would be:

instance.save(dispatch_signal=False)

How can I accomplish this?


Update

More information about what I'm doing, if anyone's interested:

  1. Generic counters are stored in a separate table
  2. Every time Django paginates an object list, it calls overriden count() method of my custom manager, which basically retrieves the static counter value for appropriate object class.
  3. Signals trigger the logic of counters update, which is a bit complicated since it checks many aspects of related models (i.e. it has to generate a visibility property based on a nested category tree). I can't put this logic in Model.save() because one counter depends on many different models. I'd like to have that logic in one piece, instead of fragments spread around.
  4. I am denormalizing some of my models, so I rewrite (duplicate) certain values across tables.
  5. For testing purposes I run my little command-extension -- Dilla, to populate random data around.
  6. I've noticed unwanted signals triggering, therefore I'd like them to run conditionally.

Hope it's clear enough. Excuse my language mistakes.

like image 428
ohnoes Avatar asked Feb 23 '09 11:02

ohnoes


2 Answers

I found simple and easy solution:

MyModel.objects.filter(pk=instance.id).update(**data)

It is due to (https://docs.djangoproject.com/en/1.5/ref/models/querysets/#update):

Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals (which are a consequence of calling Model.save()).

like image 149
Sergey Podushkin Avatar answered Sep 23 '22 10:09

Sergey Podushkin


You can disconnect and reconnect the signal. Try using a with: statement with this utility class:

class SignalBlocker(object):
    def __init__(self, signal, receiver, **kwargs):
        self.signal = signal
        self.receiver = receiver
        self.kwargs = kwargs

    def __enter__(self, *args, **kwargs):
        self.signal.disconnect(self.receiver)

    def __exit__(self, *args, **kwargs):
        self.signal.connect(self.receiver, **self.kwargs)

You can now use:

with SignalBlocker(post_save, my_post_save_handler):
    instance.save()
like image 40
Izz ad-Din Ruhulessin Avatar answered Sep 23 '22 10:09

Izz ad-Din Ruhulessin