Джанго - как я не могу послать сигнал? - PullRequest
4 голосов
/ 23 февраля 2009

Я написал несколько умных общих счетчиков и менеджеров для своих моделей (чтобы избежать select count запросов и т. Д.). Поэтому я получил некоторую тяжелую логику для post_save.

Я бы хотел предотвратить обработку сигнала, когда в этом нет необходимости. Я думаю, что идеальный интерфейс будет:

instance.save(dispatch_signal=False)

Как мне это сделать?


Обновление

Больше информации о том, что я делаю, если кому-то интересно:

  1. Общие счетчики хранятся в отдельной таблице
  2. Каждый раз, когда Django разбивает список объектов на страницы, он вызывает переопределенный метод count () моего пользовательского менеджера, который в основном получает значение статического счетчика для соответствующего класса объектов.
  3. Сигналы запускают логику обновления счетчиков, что немного сложнее, поскольку проверяет многие аспекты связанных моделей (т. Е. Оно должно генерировать свойство видимости на основе вложенного дерева категорий). Я не могу поместить эту логику в Model.save (), потому что один счетчик зависит от множества разных моделей. Я бы хотел, чтобы эта логика была цельной, а не разбросанными фрагментами.
  4. Я денормализую некоторые из моих моделей, поэтому я перезаписываю (дублирую) определенные значения в таблицах.
  5. В целях тестирования я запускаю свое маленькое расширение команды - Dilla , чтобы заполнить случайные данные вокруг.
  6. Я заметил срабатывание нежелательных сигналов, поэтому я бы хотел, чтобы они работали условно.

Надеюсь, это достаточно ясно. Извините за мои языковые ошибки.

Ответы [ 4 ]

14 голосов
/ 04 июня 2012

Вы можете отключить и повторно подключить сигнал. Попробуйте использовать оператор with: с этим служебным классом:

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)

Теперь вы можете использовать:

with SignalBlocker(post_save, my_post_save_handler):
    instance.save()
14 голосов
/ 23 февраля 2009

Быстрое и грязное решение будет:

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)

Но в остальном я настоятельно рекомендую перенести вашу логику в метод save() вашей модели.

12 голосов
/ 15 мая 2013

Я нашел простое и легкое решение:

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

Это связано с (https://docs.djangoproject.com/en/1.5/ref/models/querysets/#update):

Наконец, осознайте, что update () выполняет обновление на уровне SQL и, таким образом, не вызывает никаких методов save () в ваших моделях и не делает испускать сигналы pre_save или post_save (которые являются следствием вызов Model.save ()).

2 голосов
/ 14 сентября 2009

Вы также можете вызвать instance.save_base(raw=True) и проверить аргумент raw в обработчике сигналов pre_save или post_save:

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

Вы можете добавить немного сахара и получить идеальный интерфейс:

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

Обратите внимание, что save_base() не является частью публичного API Django, поэтому он может измениться в будущей версии.

...