Fixed #9015 -- added a signal decorator for simplifying signal connections

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13773 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Brian Rosner 2010-09-12 19:58:05 +00:00
parent 030c97b119
commit b7f60045fe
4 changed files with 66 additions and 4 deletions

View File

@ -6,4 +6,4 @@ See license.txt for original license.
Heavily modified for Django's purposes.
"""
from django.dispatch.dispatcher import Signal
from django.dispatch.dispatcher import Signal, receiver

View File

@ -235,3 +235,19 @@ class Signal(object):
for idx, (r_key, _) in enumerate(self.receivers):
if r_key == key:
del self.receivers[idx]
def receiver(signal, **kwargs):
"""
A decorator for connecting receivers to signals. Used by passing in the
signal and keyword arguments to connect::
@receiver(post_save, sender=MyModel)
def signal_receiver(sender, **kwargs):
...
"""
def _decorator(func):
signal.connect(func, **kwargs)
return func
return _decorator

View File

@ -80,7 +80,8 @@ must be able to handle those new arguments.
Connecting receiver functions
-----------------------------
Next, we'll need to connect our receiver to the signal:
There are two ways you can connect a receiever to a signal. You can take the
manual connect route:
.. code-block:: python
@ -88,6 +89,17 @@ Next, we'll need to connect our receiver to the signal:
request_finished.connect(my_callback)
Alternatively, you can use a decorator used when you define your receiver:
.. code-block:: python
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print "Request finished!"
Now, our ``my_callback`` function will be called each time a request finishes.
.. admonition:: Where should this code live?
@ -115,13 +127,13 @@ signals sent by some model:
.. code-block:: python
from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
...
pre_save.connect(my_handler, sender=MyModel)
The ``my_handler`` function will only be called when an instance of ``MyModel``
is saved.

View File

@ -3,6 +3,7 @@ Testing signals before/after saving and deleting.
"""
from django.db import models
from django.dispatch import receiver
class Person(models.Model):
first_name = models.CharField(max_length=20)
@ -11,6 +12,13 @@ class Person(models.Model):
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
class Car(models.Model):
make = models.CharField(max_length=20)
model = models.CharField(max_length=20)
def __unicode__(self):
return u"%s %s" % (self.make, self.model)
def pre_save_test(signal, sender, instance, **kwargs):
print 'pre_save signal,', instance
if kwargs.get('raw'):
@ -52,22 +60,44 @@ __test__ = {'API_TESTS':"""
>>> models.signals.pre_delete.connect(pre_delete_test)
>>> models.signals.post_delete.connect(post_delete_test)
# throw a decorator syntax receiver into the mix
>>> @receiver(models.signals.pre_save)
... def pre_save_decorator_test(signal, sender, instance, **kwargs):
... print "pre_save signal decorator,", instance
# throw a decorator syntax receiver into the mix
>>> @receiver(models.signals.pre_save, sender=Car)
... def pre_save_decorator_sender_test(signal, sender, instance, **kwargs):
... print "pre_save signal decorator sender,", instance
>>> p1 = Person(first_name='John', last_name='Smith')
>>> p1.save()
pre_save signal, John Smith
pre_save signal decorator, John Smith
post_save signal, John Smith
Is created
>>> p1.first_name = 'Tom'
>>> p1.save()
pre_save signal, Tom Smith
pre_save signal decorator, Tom Smith
post_save signal, Tom Smith
Is updated
# Car signal (sender defined)
>>> c1 = Car(make="Volkswagon", model="Passat")
>>> c1.save()
pre_save signal, Volkswagon Passat
pre_save signal decorator, Volkswagon Passat
pre_save signal decorator sender, Volkswagon Passat
post_save signal, Volkswagon Passat
Is created
# Calling an internal method purely so that we can trigger a "raw" save.
>>> p1.save_base(raw=True)
pre_save signal, Tom Smith
Is raw
pre_save signal decorator, Tom Smith
post_save signal, Tom Smith
Is updated
Is raw
@ -82,12 +112,14 @@ instance.id is None: False
>>> p2.id = 99999
>>> p2.save()
pre_save signal, James Jones
pre_save signal decorator, James Jones
post_save signal, James Jones
Is created
>>> p2.id = 99998
>>> p2.save()
pre_save signal, James Jones
pre_save signal decorator, James Jones
post_save signal, James Jones
Is created
@ -104,6 +136,8 @@ instance.id is None: False
>>> models.signals.pre_delete.disconnect(pre_delete_test)
>>> models.signals.post_save.disconnect(post_save_test)
>>> models.signals.pre_save.disconnect(pre_save_test)
>>> models.signals.pre_save.disconnect(pre_save_decorator_test)
>>> models.signals.pre_save.disconnect(pre_save_decorator_sender_test, sender=Car)
# Check that all our signals got disconnected properly.
>>> post_signals = (len(models.signals.pre_save.receivers),