123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- =======
- Signals
- =======
- .. module:: django.dispatch
- :synopsis: Signal dispatch
- Django includes a "signal dispatcher" which helps 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.
- For example, a third-party app can register to be notified of settings
- changes::
- from django.apps import AppConfig
- from django.core.signals import setting_changed
- def my_callback(sender, **kwargs):
- print("Setting changed!")
- class MyAppConfig(AppConfig):
- ...
- def ready(self):
- setting_changed.connect(my_callback)
- Django's :doc:`built-in signals </ref/signals>` let user code get notified of
- certain actions.
- You can also define and send your own custom signals. See
- :ref:`defining-and-sending-signals` below.
- .. warning::
- Signals give the appearance of loose coupling, but they can quickly lead to
- code that is hard to understand, adjust and debug.
- Where possible you should opt for directly calling the handling code,
- rather than dispatching via a signal.
- Listening to signals
- ====================
- To receive a signal, register a *receiver* function using the
- :meth:`Signal.connect` method. The receiver function is called when the signal
- is sent. All of the signal's receiver functions are called one at a time, in
- the order they were registered.
- .. 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 :ref:`a bit later <connecting-to-specific-signals>`, 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)``.
- 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.
- Receivers may also be asynchronous functions, with the same signature but
- declared using ``async def``::
- async def my_callback(sender, **kwargs):
- await asyncio.sleep(5)
- print("Request finished!")
- Signals can be sent either synchronously or asynchronously, and receivers will
- automatically be adapted to the correct call-style. See :ref:`sending signals
- <sending-signals>` for more information.
- .. _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, **kwargs)
- :param signal: A signal or a list of signals to connect a function to.
- :param kwargs: Wildcard keyword arguments to pass to a
- :ref:`function <receiver-functions>`.
- 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 :ref:`configuration class <configuring-applications-ref>`. If
- you're using the :func:`receiver` decorator, import the ``signals``
- submodule inside :meth:`~django.apps.AppConfig.ready`, this will implicitly
- connect signal handlers::
- from django.apps import AppConfig
- from django.core.signals import request_finished
- class MyAppConfig(AppConfig):
- ...
- def ready(self):
- # Implicitly connect signal handlers decorated with @receiver.
- from . import signals
- # Explicitly connect a signal handler.
- request_finished.connect(signals.my_callback)
- .. 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 as many times for a signal event. For example, the
- :meth:`~django.apps.AppConfig.ready` method may be executed more than once
- during testing. More generally, this occurs everywhere your project imports the
- module where you define the signals, because signal registration runs as many
- times as it is imported.
- 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:
- Defining and sending signals
- ============================
- Your applications can take advantage of the signal infrastructure and provide
- its own signals.
- .. admonition:: When to use custom signals
- Signals are implicit function calls which make debugging harder. If the
- sender and receiver of your custom signal are both within your project,
- you're better off using an explicit function call.
- Defining signals
- ----------------
- .. class:: Signal()
- All signals are :class:`django.dispatch.Signal` instances.
- For example::
- import django.dispatch
- pizza_done = django.dispatch.Signal()
- This declares a ``pizza_done`` signal.
- .. _sending-signals:
- Sending signals
- ---------------
- There are two ways to send signals synchronously in Django.
- .. method:: Signal.send(sender, **kwargs)
- .. method:: Signal.send_robust(sender, **kwargs)
- Signals may also be sent asynchronously.
- .. method:: Signal.asend(sender, **kwargs)
- .. method:: Signal.asend_robust(sender, **kwargs)
- To send a signal, call either :meth:`Signal.send`, :meth:`Signal.send_robust`,
- :meth:`await Signal.asend()<Signal.asend>`, or
- :meth:`await Signal.asend_robust() <Signal.asend_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:
- ...
- def send_pizza(self, toppings, size):
- pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
- ...
- All four methods 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()``.
- ``asend()`` is similar to ``send()``, but it is a coroutine that must be
- awaited::
- async def asend_pizza(self, toppings, size):
- await pizza_done.asend(sender=self.__class__, toppings=toppings, size=size)
- ...
- Whether synchronous or asynchronous, receivers will be correctly adapted to
- whether ``send()`` or ``asend()`` is used. Synchronous receivers will be
- called using :func:`~.sync_to_async` when invoked via ``asend()``. Asynchronous
- receivers will be called using :func:`~.async_to_sync` when invoked via
- ``sync()``. Similar to the :ref:`case for middleware <async_performance>`,
- there is a small performance cost to adapting receivers in this way. Note that
- in order to reduce the number of sync/async calling-style switches within a
- ``send()`` or ``asend()`` call, the receivers are grouped by whether or not
- they are async before being called. This means that an asynchronous receiver
- registered before a synchronous receiver may be executed after the synchronous
- receiver. In addition, async receivers are executed concurrently using
- ``asyncio.gather()``.
- All built-in signals, except those in the async request-response cycle, are
- dispatched using :meth:`Signal.send`.
- 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. When ``sender``
- is passed as a lazy reference to ``<app label>.<model>``, this method always
- returns ``None``.
- The ``receiver`` argument indicates the registered receiver to disconnect. It
- may be ``None`` if ``dispatch_uid`` is used to identify the receiver.
|