summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Medvinsky <dmedvinsky@gmail.com>2012-06-08 14:00:51 +0400
committerFlorian Apolloner <florian@apolloner.eu>2012-06-23 16:31:16 +0200
commitd4da08375b634544b95859d4d4667b8f05e3a29a (patch)
treecdd83ab9b4a15ef938c9811c3e5f02edaf34a4c2
parent946d3d9f84ea7979a4abf0857e4aa7ee33576303 (diff)
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): ...
-rw-r--r--django/dispatch/dispatcher.py11
-rw-r--r--docs/releases/1.5.txt3
-rw-r--r--docs/topics/signals.txt13
-rw-r--r--tests/regressiontests/dispatch/tests/__init__.py2
-rw-r--r--tests/regressiontests/dispatch/tests/test_dispatcher.py30
5 files changed, 52 insertions, 7 deletions
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index 54e71c01cc..8f57b185c3 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -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):
- 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
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index 719433f8fe..2f20f5f9f9 100644
--- a/docs/releases/1.5.txt
+++ b/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
=====================================
diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt
index 3ef68316a9..fa668cc8c7 100644
--- a/docs/topics/signals.txt
+++ b/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
diff --git a/tests/regressiontests/dispatch/tests/__init__.py b/tests/regressiontests/dispatch/tests/__init__.py
index 447975ab85..b6d26217e1 100644
--- a/tests/regressiontests/dispatch/tests/__init__.py
+++ b/tests/regressiontests/dispatch/tests/__init__.py
@@ -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
diff --git a/tests/regressiontests/dispatch/tests/test_dispatcher.py b/tests/regressiontests/dispatch/tests/test_dispatcher.py
index 319d6553a0..5f7094d5fa 100644
--- a/tests/regressiontests/dispatch/tests/test_dispatcher.py
+++ b/tests/regressiontests/dispatch/tests/test_dispatcher.py
@@ -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)