mirror of https://github.com/django/django.git
284 lines
10 KiB
Plaintext
284 lines
10 KiB
Plaintext
=======
|
|
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 </ref/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 or queryset's :meth:`~django.db.models.query.QuerySet.delete`
|
|
method is called.
|
|
|
|
* :data:`django.db.models.signals.m2m_changed`
|
|
|
|
Sent when a :class:`~django.db.models.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 </ref/signals>` 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::
|
|
|
|
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
|
|
-----------------------------
|
|
|
|
There are two ways you can connect a receiver to a signal. You can take the
|
|
manual connect route::
|
|
|
|
from django.core.signals import request_finished
|
|
|
|
request_finished.connect(my_callback)
|
|
|
|
Alternatively, you can use a :func:`receiver` decorator:
|
|
|
|
.. function:: receiver(signal)
|
|
|
|
:param signal: A signal or a list of signals to connect a function to.
|
|
|
|
Here's how you connect with the decorator::
|
|
|
|
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?
|
|
|
|
Strictly speaking, signal handling and registration code can live anywhere
|
|
you like, although it's recommended to avoid the application's root module
|
|
and its ``models`` module to minimize side-effects of importing code.
|
|
|
|
In practice, signal handlers are usually defined in a ``signals``
|
|
submodule of the application they relate to. Signal receivers are
|
|
connected in the :meth:`~django.apps.AppConfig.ready` method of your
|
|
application configuration class. If you're using the :func:`receiver`
|
|
decorator, simply import the ``signals`` submodule inside
|
|
:meth:`~django.apps.AppConfig.ready`.
|
|
|
|
.. note::
|
|
|
|
The :meth:`~django.apps.AppConfig.ready` method may be executed more than
|
|
once during testing, so you may want to :ref:`guard your signals from
|
|
duplication <preventing-duplicate-signals>`, especially if you're planning
|
|
to send them within tests.
|
|
|
|
.. _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::
|
|
|
|
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):
|
|
...
|
|
|
|
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 </ref/signals>` for details of each
|
|
particular signal.
|
|
|
|
.. _preventing-duplicate-signals:
|
|
|
|
Preventing duplicate signals
|
|
----------------------------
|
|
|
|
In some circumstances, the code connecting receivers to signals may run
|
|
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 email 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::
|
|
|
|
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. This is purely documentational, however, as there is nothing that
|
|
checks that the signal actually provides these arguments to its listeners.
|
|
|
|
For example::
|
|
|
|
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 signals in Django.
|
|
|
|
.. method:: Signal.send(sender, **kwargs)
|
|
.. method:: Signal.send_robust(sender, **kwargs)
|
|
|
|
To send a signal, call either :meth:`Signal.send` (all built-in signals use
|
|
this) or :meth:`Signal.send_robust`. You must provide the ``sender`` argument
|
|
(which is a class most of the time) and may provide as many other keyword
|
|
arguments as you like.
|
|
|
|
For example, here's how sending our ``pizza_done`` signal might look::
|
|
|
|
class PizzaStore(object):
|
|
...
|
|
|
|
def send_pizza(self, toppings, size):
|
|
pizza_done.send(sender=self.__class__, 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.
|
|
|
|
The tracebacks are present on the ``__traceback__`` attribute of the errors
|
|
returned when calling ``send_robust()``.
|
|
|
|
Disconnecting signals
|
|
=====================
|
|
|
|
.. method:: Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)
|
|
|
|
To disconnect a receiver from a signal, call :meth:`Signal.disconnect`. The
|
|
arguments are as described in :meth:`.Signal.connect`. The method returns
|
|
``True`` if a receiver was disconnected and ``False`` if not.
|
|
|
|
The ``receiver`` argument indicates the registered receiver to disconnect. It
|
|
may be ``None`` if ``dispatch_uid`` is used to identify the receiver.
|
|
|
|
.. deprecated:: 1.9
|
|
|
|
The ``weak`` argument is deprecated as it has no effect. It will be removed
|
|
in Django 2.0.
|