diff -r 5ff1fc726848 -r c6bca38c1cbf parts/django/docs/topics/signals.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/parts/django/docs/topics/signals.txt Sat Jan 08 11:20:57 2011 +0530 @@ -0,0 +1,255 @@ +======= +Signals +======= + +.. module:: django.dispatch + :synopsis: Signal dispatch + +Django includes a "signal dispatcher" which helps allow decoupled applications +get notified when actions occur elsewhere in the framework. In a nutshell, +signals allow certain *senders* to notify a set of *receivers* that some action +has taken place. They're especially useful when many pieces of code may be +interested in the same events. + +Django provides a :doc:`set of built-in signals ` that let user +code get notified by Django itself of certain actions. These include some useful +notifications: + + * :data:`django.db.models.signals.pre_save` & + :data:`django.db.models.signals.post_save` + + Sent before or after a model's :meth:`~django.db.models.Model.save` method + is called. + + * :data:`django.db.models.signals.pre_delete` & + :data:`django.db.models.signals.post_delete` + + Sent before or after a model's :meth:`~django.db.models.Model.delete` + method is called. + + * :data:`django.db.models.signals.m2m_changed` + + Sent when a :class:`ManyToManyField` on a model is changed. + + * :data:`django.core.signals.request_started` & + :data:`django.core.signals.request_finished` + + Sent when Django starts or finishes an HTTP request. + +See the :doc:`built-in signal documentation ` for a complete list, +and a complete explanation of each signal. + +You can also `define and send your own custom signals`_; see below. + +.. _define and send your own custom signals: `defining and sending signals`_ + +Listening to signals +==================== + +To receive a signal, you need to register a *receiver* function that gets +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. + + :param weak: Django stores signal handlers as weak references by + default. Thus, if your receiver is a local function, it may be + garbage collected. To prevent this, pass ``weak=False`` when you call + the signal's ``connect()`` method. + + :param dispatch_uid: A unique identifier for a signal receiver in cases + where duplicate signals may be sent. See + :ref:`preventing-duplicate-signals` for more information. + +Let's see how this works by registering a signal that +gets called after each HTTP request is finished. We'll be connecting to the +:data:`~django.core.signals.request_finished` signal. + +.. _receiver-functions: + +Receiver functions +------------------ + +First, we need to define a receiver function. A receiver can be any Python +function or method: + +.. code-block:: python + + def my_callback(sender, **kwargs): + print "Request finished!" + +Notice that the function takes a ``sender`` argument, along with wildcard +keyword arguments (``**kwargs``); all signal handlers must take these arguments. + +We'll look at senders `a bit later`_, but right now look at the ``**kwargs`` +argument. All signals send keyword arguments, and may change those keyword +arguments at any time. In the case of +:data:`~django.core.signals.request_finished`, it's documented as sending no +arguments, which means we might be tempted to write our signal handling as +``my_callback(sender)``. + +.. _a bit later: `connecting to signals sent by specific senders`_ + +This would be wrong -- in fact, Django will throw an error if you do so. That's +because at any point arguments could get added to the signal and your receiver +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: + +.. code-block:: python + + from django.core.signals import request_finished + + request_finished.connect(my_callback) + +Now, our ``my_callback`` function will be called each time a request finishes. + +.. admonition:: Where should this code live? + + You can put signal handling and registration code anywhere you like. + However, you'll need to make sure that the module it's in gets imported + early on so that the signal handling gets registered before any signals need + to be sent. This makes your app's ``models.py`` a good place to put + registration of signal handlers. + +.. _connecting-to-specific-signals: + +Connecting to signals sent by specific senders +---------------------------------------------- + +Some signals get sent many times, but you'll only be interested in receiving a +certain subset of those signals. For example, consider the +:data:`django.db.models.signals.pre_save` signal sent before a model gets saved. +Most of the time, you don't need to know when *any* model gets saved -- just +when one *specific* model is saved. + +In these cases, you can register to receive signals sent only by particular +senders. In the case of :data:`django.db.models.signals.pre_save`, the sender +will be the model class being saved, so you can indicate that you only want +signals sent by some model: + +.. code-block:: python + + from django.db.models.signals import pre_save + from myapp.models import 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. + +Different signals use different objects as their senders; you'll need to consult +the :doc:`built-in signal documentation ` for details of each +particular signal. + +.. _preventing-duplicate-signals: + +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. + +If this behavior is problematic (such as when using signals to +send an e-mail whenever a model is saved), pass a unique identifier as +the ``dispatch_uid`` argument to identify your receiver function. This +identifier will usually be a string, although any hashable object will +suffice. The end result is that your receiver function will only be +bound to the signal once for each unique ``dispatch_uid`` value. + +.. code-block:: python + + from django.core.signals import request_finished + + request_finished.connect(my_callback, dispatch_uid="my_unique_identifier") + +Defining and sending signals +============================ + +Your applications can take advantage of the signal infrastructure and provide +its own signals. + +Defining signals +---------------- + +.. class:: Signal([providing_args=list]) + +All signals are :class:`django.dispatch.Signal` instances. The +``providing_args`` is a list of the names of arguments the signal will provide +to listeners. + +For example: + +.. code-block:: python + + import django.dispatch + + pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) + +This declares a ``pizza_done`` signal that will provide receivers with +``toppings`` and ``size`` arguments. + +Remember that you're allowed to change this list of arguments at any time, so getting the API right on the first try isn't necessary. + +Sending signals +--------------- + +There are two ways to send send signals in Django. + +.. method:: Signal.send(sender, **kwargs) +.. method:: Signal.send_robust(sender, **kwargs) + +To send a signal, call either :meth:`Signal.send` or :meth:`Signal.send_robust`. +You must provide the ``sender`` argument, and may provide as many other keyword +arguments as you like. + +For example, here's how sending our ``pizza_done`` signal might look: + +.. code-block:: python + + class PizzaStore(object): + ... + + def send_pizza(self, toppings, size): + pizza_done.send(sender=self, toppings=toppings, size=size) + ... + +Both ``send()`` and ``send_robust()`` return a list of tuple pairs +``[(receiver, response), ... ]``, representing the list of called receiver +functions and their response values. + +``send()`` differs from ``send_robust()`` in how exceptions raised by receiver +functions are handled. ``send()`` does *not* catch any exceptions raised by +receivers; it simply allows errors to propagate. Thus not all receivers may +be notified of a signal in the face of an error. + +``send_robust()`` catches all errors derived from Python's ``Exception`` class, +and ensures all receivers are notified of the signal. If an error occurs, the +error instance is returned in the tuple pair for the receiver that raised the error. + +Disconnecting signals +===================== + +.. method:: Signal.disconnect([receiver=None, sender=None, weak=True, dispatch_uid=None]) + +To disconnect a receiver from a signal, call :meth:`Signal.disconnect`. The +arguments are as described in :meth:`.Signal.connect`. + +The *receiver* argument indicates the registered receiver to disconnect. It may +be ``None`` if ``dispatch_uid`` is used to identify the receiver.