Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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):
        ...
  • Loading branch information...
commit d4da08375b634544b95859d4d4667b8f05e3a29a 1 parent 946d3d9
@dmedvinsky dmedvinsky authored apollo13 committed
View
11 django/dispatch/dispatcher.py
@@ -257,14 +257,21 @@ def _remove_receiver(self, receiver):
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):
- signal.connect(func, **kwargs)
+ if isinstance(signal, (list, tuple)):
+ [s.connect(func, **kwargs) for s in signal]
+ else:
+ signal.connect(func, **kwargs)
return func
return _decorator
View
3  docs/releases/1.5.txt
@@ -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
=====================================
View
13 docs/topics/signals.txt
@@ -52,10 +52,10 @@ called when the signal is sent by using the
:meth:`.Signal.connect` method:
.. method:: Signal.connect(receiver, [sender=None, weak=True, dispatch_uid=None])
-
+
:param receiver: The callback function which will be connected to this
signal. See :ref:`receiver-functions` for more information.
-
+
:param sender: Specifies a particular sender to receive signals from. See
:ref:`connecting-to-specific-signals` for more information.
@@ -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.
@@ -182,7 +189,7 @@ Preventing duplicate signals
In some circumstances, the module in which you are connecting signals may be
imported multiple times. This can cause your receiver function to be
registered more than once, and thus called multiples times for a single signal
-event.
+event.
If this behavior is problematic (such as when using signals to
send an email whenever a model is saved), pass a unique identifier as
View
2  tests/regressiontests/dispatch/tests/__init__.py
@@ -4,5 +4,5 @@
from __future__ import absolute_import
-from .test_dispatcher import DispatcherTests
+from .test_dispatcher import DispatcherTests, ReceiverTestCase
from .test_saferef import SaferefTests
View
30 tests/regressiontests/dispatch/tests/test_dispatcher.py
@@ -2,7 +2,7 @@
import sys
import time
-from django.dispatch import Signal
+from django.dispatch import Signal, receiver
from django.utils import unittest
@@ -33,6 +33,8 @@ def a(self, val, **kwargs):
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 @@ def testDisconnection(self):
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)
Please sign in to comment.
Something went wrong with that request. Please try again.