Django - how do I _not_ dispatch a signal?

可紊 提交于 2019-12-21 19:19:42

问题


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.


回答1:


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.




回答2:


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()



回答3:


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()).




回答4:


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.



来源:https://stackoverflow.com/questions/577376/django-how-do-i-not-dispatch-a-signal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!