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.
|
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):
|
for idx, (r_key, _) in enumerate(self.receivers):
|
||||||
if r_key == key:
|
if r_key == key:
|
||||||
del self.receivers[idx]
|
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
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -88,6 +89,17 @@ Next, we'll need to connect our receiver to the signal:
|
||||||
|
|
||||||
request_finished.connect(my_callback)
|
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.
|
Now, our ``my_callback`` function will be called each time a request finishes.
|
||||||
|
|
||||||
.. admonition:: Where should this code live?
|
.. admonition:: Where should this code live?
|
||||||
|
@ -115,13 +127,13 @@ signals sent by some model:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from django.db.models.signals import pre_save
|
from django.db.models.signals import pre_save
|
||||||
|
from django.dispatch import receiver
|
||||||
from myapp.models import MyModel
|
from myapp.models import MyModel
|
||||||
|
|
||||||
|
@receiver(pre_save, sender=MyModel)
|
||||||
def my_handler(sender, **kwargs):
|
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``
|
The ``my_handler`` function will only be called when an instance of ``MyModel``
|
||||||
is saved.
|
is saved.
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ Testing signals before/after saving and deleting.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
first_name = models.CharField(max_length=20)
|
first_name = models.CharField(max_length=20)
|
||||||
|
@ -11,6 +12,13 @@ class Person(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u"%s %s" % (self.first_name, self.last_name)
|
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):
|
def pre_save_test(signal, sender, instance, **kwargs):
|
||||||
print 'pre_save signal,', instance
|
print 'pre_save signal,', instance
|
||||||
if kwargs.get('raw'):
|
if kwargs.get('raw'):
|
||||||
|
@ -52,22 +60,44 @@ __test__ = {'API_TESTS':"""
|
||||||
>>> models.signals.pre_delete.connect(pre_delete_test)
|
>>> models.signals.pre_delete.connect(pre_delete_test)
|
||||||
>>> models.signals.post_delete.connect(post_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 = Person(first_name='John', last_name='Smith')
|
||||||
>>> p1.save()
|
>>> p1.save()
|
||||||
pre_save signal, John Smith
|
pre_save signal, John Smith
|
||||||
|
pre_save signal decorator, John Smith
|
||||||
post_save signal, John Smith
|
post_save signal, John Smith
|
||||||
Is created
|
Is created
|
||||||
|
|
||||||
>>> p1.first_name = 'Tom'
|
>>> p1.first_name = 'Tom'
|
||||||
>>> p1.save()
|
>>> p1.save()
|
||||||
pre_save signal, Tom Smith
|
pre_save signal, Tom Smith
|
||||||
|
pre_save signal decorator, Tom Smith
|
||||||
post_save signal, Tom Smith
|
post_save signal, Tom Smith
|
||||||
Is updated
|
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.
|
# Calling an internal method purely so that we can trigger a "raw" save.
|
||||||
>>> p1.save_base(raw=True)
|
>>> p1.save_base(raw=True)
|
||||||
pre_save signal, Tom Smith
|
pre_save signal, Tom Smith
|
||||||
Is raw
|
Is raw
|
||||||
|
pre_save signal decorator, Tom Smith
|
||||||
post_save signal, Tom Smith
|
post_save signal, Tom Smith
|
||||||
Is updated
|
Is updated
|
||||||
Is raw
|
Is raw
|
||||||
|
@ -82,12 +112,14 @@ instance.id is None: False
|
||||||
>>> p2.id = 99999
|
>>> p2.id = 99999
|
||||||
>>> p2.save()
|
>>> p2.save()
|
||||||
pre_save signal, James Jones
|
pre_save signal, James Jones
|
||||||
|
pre_save signal decorator, James Jones
|
||||||
post_save signal, James Jones
|
post_save signal, James Jones
|
||||||
Is created
|
Is created
|
||||||
|
|
||||||
>>> p2.id = 99998
|
>>> p2.id = 99998
|
||||||
>>> p2.save()
|
>>> p2.save()
|
||||||
pre_save signal, James Jones
|
pre_save signal, James Jones
|
||||||
|
pre_save signal decorator, James Jones
|
||||||
post_save signal, James Jones
|
post_save signal, James Jones
|
||||||
Is created
|
Is created
|
||||||
|
|
||||||
|
@ -104,6 +136,8 @@ instance.id is None: False
|
||||||
>>> models.signals.pre_delete.disconnect(pre_delete_test)
|
>>> models.signals.pre_delete.disconnect(pre_delete_test)
|
||||||
>>> models.signals.post_save.disconnect(post_save_test)
|
>>> models.signals.post_save.disconnect(post_save_test)
|
||||||
>>> models.signals.pre_save.disconnect(pre_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.
|
# Check that all our signals got disconnected properly.
|
||||||
>>> post_signals = (len(models.signals.pre_save.receivers),
|
>>> post_signals = (len(models.signals.pre_save.receivers),
|
||||||
|
|
Loading…
Reference in New Issue