views:

332

answers:

2

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.

+7  A: 

A quick and dirty solution would be:

from django.db.models.signals import post_save
from somewhere_in_my_app import my_post_save_handler

post_save.disconnect(my_post_save_handler)
instance.save()
post_save.connect(my_post_save_handler)

But otherwise i strongly recommend moving your logic into the save() method of your model.

RommeDeSerieux
+1 for moving logic into `save()`.
Török Gábor
A: 

You can also call instance.save_base(raw=True) and check for the raw argument in your pre_save or post_save signal handler:

def my_post_save_handler(instance, raw, **kwargs):
    if not raw:
        heavy_logic()

You can add some sugar and get your perfect interface:

class MyModel:
    def save(self, dispatch_signal=True, **kwargs):
        self.save_base(raw=not dispatch_signal, **kwargs)

Note that save_base() is not part of the public API of Django, so it might change in a future version.

akaihola