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:
parent
030c97b119
commit
b7f60045fe
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue