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):
        ...
This commit is contained in:
Dmitry Medvinsky 2012-06-08 14:00:51 +04:00 committed by Florian Apolloner
parent 946d3d9f84
commit d4da08375b
5 changed files with 52 additions and 7 deletions

View File

@ -257,14 +257,21 @@ class Signal(object):
def receiver(signal, **kwargs): def receiver(signal, **kwargs):
""" """
A decorator for connecting receivers to signals. Used by passing in the 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) @receiver(post_save, sender=MyModel)
def signal_receiver(sender, **kwargs): def signal_receiver(sender, **kwargs):
... ...
@receiver([post_save, post_delete], sender=MyModel)
def signals_receiver(sender, **kwargs):
...
""" """
def _decorator(func): 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 func
return _decorator return _decorator

View File

@ -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 * In the localflavor for Canada, "pq" was added to the acceptable codes for
Quebec. It's an old abbreviation. 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 Backwards incompatible changes in 1.5
===================================== =====================================

View File

@ -129,10 +129,17 @@ receiver:
Now, our ``my_callback`` function will be called each time a request finishes. 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 .. versionadded:: 1.3
The ``receiver`` decorator was added in Django 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? .. admonition:: Where should this code live?
You can put signal handling and registration code anywhere you like. You can put signal handling and registration code anywhere you like.

View File

@ -4,5 +4,5 @@ Unit-tests for the dispatch project
from __future__ import absolute_import from __future__ import absolute_import
from .test_dispatcher import DispatcherTests from .test_dispatcher import DispatcherTests, ReceiverTestCase
from .test_saferef import SaferefTests from .test_saferef import SaferefTests

View File

@ -2,7 +2,7 @@ import gc
import sys import sys
import time import time
from django.dispatch import Signal from django.dispatch import Signal, receiver
from django.utils import unittest from django.utils import unittest
@ -33,6 +33,8 @@ class Callable(object):
return val return val
a_signal = Signal(providing_args=["val"]) a_signal = Signal(providing_args=["val"])
b_signal = Signal(providing_args=["val"])
c_signal = Signal(providing_args=["val"])
class DispatcherTests(unittest.TestCase): class DispatcherTests(unittest.TestCase):
"""Test suite for dispatcher (barely started)""" """Test suite for dispatcher (barely started)"""
@ -123,3 +125,29 @@ class DispatcherTests(unittest.TestCase):
garbage_collect() garbage_collect()
a_signal.disconnect(receiver_3) a_signal.disconnect(receiver_3)
self._testIsClean(a_signal) 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)