Объединение нескольких сигналов post_save с одним приемником

Поэтому я прочитал исходный код Django (сообщение 1.5), что теперь вы можете зарегистрировать несколько множественных сигналов для функции приемника:

def receiver(signal, **kwargs):
    """
    A decorator for connecting receivers to signals. Used by passing in the
    signal (or list of signals) and keyword arguments to connect::

        @receiver(post_save, sender=MyModel)
        def signal_receiver(sender, **kwargs):
            ...

        @receiver([post_save, post_delete], sender=MyModel)
        def signals_receiver(sender, **kwargs):
            ...

    """
    ... implementation code...

Тем не менее, я хочу зарегистрировать несколько сигналов post_save от разных отправителей к одной и той же функции. Прямо сейчас, я просто звоню

post_save.connect(fn_name, model_name)

для каждой модели, которая у меня есть. Есть ли лучший способ сделать это с помощью новой возможности декодера Django 1.5 @receiver?

Ответ 1

Вы можете использовать это с помощью декоратора @receiver:

@receiver(post_save, sender=Model1)
@receiver(post_save, sender=Model2)
@receiver(post_save, sender=Model3)
def my_signal_handle(sender , **kwargs)
    # some code here

Ответ 2

В документации Django на приемниках приемники по умолчанию не должны подключаться к определенному отправителю. Так что вы описываете функциональность Django по умолчанию.

Другими словами, для этого, используя @receiver вы просто не указываете отправителя в декораторе. Например:

@receiver(post_save) # instead of @receiver(post_save, sender=Rebel)
def set_winner(sender, instance=None, created=False, **kwargs):
    list_of_models = ('Rebel', 'Stormtrooper', 'Battleground')
    if sender.__name__ in list_of_models: # this is the dynamic part you want
        if created: # only run when object is first created
            ... set the winner ...

Это предполагает модели, которые выглядят так:

class Rebel(models.Model):
    ...

class Stormtrooper(models.Model):
    ...

class Battleground(models.Model):
    ...

Ответ 3

Вы можете пропустить имя model_name и вы будете подключаться ко всем моделям post_save. Затем вы можете фильтровать, если вы находитесь в правильной модели в обработчике:

post_save.connect(foo)

def foo(sender, **kwargs):
    if sender not in [FooModel, BarModel]:
        return
    ... actual code ...

или вы можете фильтровать на основе поля в модели:

def foo(sender, **kwargs):
    if not getattr(sender, 'process_by_foo', False):
        return
    ... actual code ...

Ответ 4

def receiver_with_multiple_senders(signal, senders, **kwargs):
    """
    Based on django.dispatch.dispatcher.receiver

    Allows multiple senders so we can avoid using a stack of
    regular receiver decorators with one sender each.
    """

    def decorator(receiver_func):
        for sender in senders:
            if isinstance(signal, (list, tuple)):
                for s in signal:
                    s.connect(receiver_func, sender=sender, **kwargs)
            else:
                signal.connect(receiver_func, sender=sender, **kwargs)

        return receiver_func

    return decorator