Commit d4da0837 authored by Dmitry Medvinsky's avatar Dmitry Medvinsky Committed by Florian Apolloner
Browse files

Fixed #18454 -- Added ability to pass a list of signals to `receiver`.

Added ability to use receiver decorator in the following way:

    @receiver([post_save, post_delete], sender=MyModel)
    def signals_receiver(sender, **kwargs):
        ...
parent 946d3d9f
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -257,14 +257,21 @@ class Signal(object):
def receiver(signal, **kwargs):
    """
    A decorator for connecting receivers to signals. Used by passing in the
    signal and keyword arguments to connect::
    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):
            ...

    """
    def _decorator(func):
        if isinstance(signal, (list, tuple)):
            [s.connect(func, **kwargs) for s in signal]
        else:
            signal.connect(func, **kwargs)
        return func
    return _decorator
+3 −0
Original line number Diff line number Diff line
@@ -103,6 +103,9 @@ Django 1.5 also includes several smaller improvements worth noting:
* In the localflavor for Canada, "pq" was added to the acceptable codes for
  Quebec. It's an old abbreviation.

* The :ref:`receiver <connecting-receiver-functions>` decorator is now able to
  connect to more than one signal by supplying a list of signals.

Backwards incompatible changes in 1.5
=====================================

+10 −3
Original line number Diff line number Diff line
@@ -129,10 +129,17 @@ receiver:

Now, our ``my_callback`` function will be called each time a request finishes.

Note that ``receiver`` can also take a list of signals to connect a function
to.

.. versionadded:: 1.3

The ``receiver`` decorator was added in Django 1.3.

.. versionchanged:: 1.5

The ability to pass a list of signals was added.

.. admonition:: Where should this code live?

    You can put signal handling and registration code anywhere you like.
+1 −1
Original line number Diff line number Diff line
@@ -4,5 +4,5 @@ Unit-tests for the dispatch project

from __future__ import absolute_import

from .test_dispatcher import DispatcherTests
from .test_dispatcher import DispatcherTests, ReceiverTestCase
from .test_saferef import SaferefTests
+29 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@ import gc
import sys
import time

from django.dispatch import Signal
from django.dispatch import Signal, receiver
from django.utils import unittest


@@ -33,6 +33,8 @@ class Callable(object):
        return val

a_signal = Signal(providing_args=["val"])
b_signal = Signal(providing_args=["val"])
c_signal = Signal(providing_args=["val"])

class DispatcherTests(unittest.TestCase):
    """Test suite for dispatcher (barely started)"""
@@ -123,3 +125,29 @@ class DispatcherTests(unittest.TestCase):
        garbage_collect()
        a_signal.disconnect(receiver_3)
        self._testIsClean(a_signal)


class ReceiverTestCase(unittest.TestCase):
    """
    Test suite for receiver.

    """
    def testReceiverSingleSignal(self):
        @receiver(a_signal)
        def f(val, **kwargs):
            self.state = val
        self.state = False
        a_signal.send(sender=self, val=True)
        self.assertTrue(self.state)

    def testReceiverSignalList(self):
        @receiver([a_signal, b_signal, c_signal])
        def f(val, **kwargs):
            self.state.append(val)
        self.state = []
        a_signal.send(sender=self, val='a')
        c_signal.send(sender=self, val='c')
        b_signal.send(sender=self, val='b')
        self.assertIn('a', self.state)
        self.assertIn('b', self.state)
        self.assertIn('c', self.state)