Browse Source

Fixed #8326: added signals documentation.

Thanks to MercuryTide's signal whitepaper from 2006 (http://www.mercurytide.co.uk/whitepapers/django-signals/) for inspiration and ideas for organization, and to the folks who got the Signals wiki page together for some of the content.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@8590 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Jacob Kaplan-Moss 16 years ago
parent
commit
6f29ad31b5

+ 1 - 0
docs/ref/contrib/comments/index.txt

@@ -208,5 +208,6 @@ More information
    :maxdepth: 1
 
    settings
+   signals
    upgrade
 

+ 88 - 0
docs/ref/contrib/comments/signals.txt

@@ -0,0 +1,88 @@
+.. _ref-contrib-comments-signals:
+
+================================
+Signals sent by the comments app
+================================
+
+.. module:: django.contrib.comments.signals
+   :synopsis: Signals sent by the comment module.
+
+The comment app sends a series of :ref:`signals <topics-signals>` to allow for
+comment moderation and similar activities. See :ref:`the introduction to signals
+<topics-signals>` for information about how to register for and receive these
+signals.
+
+comment_will_be_posted
+======================
+
+.. data:: django.contrib.comments.signals.comment_will_be_posted
+
+Sent just before a comment will be saved, after it's been sanity checked and
+submitted. This can be used to modify the comment (in place) with posting
+details or other such actions.
+
+If any receiver returns ``False`` the comment will be discarded and a 403 (not
+allowed) response will be returned.
+
+This signal is sent at more or less the same time (just before, actually) as the
+``Comment`` object's :data:`~django.db.models.signals.pre_save` signal.
+
+Arguments sent with this signal:
+    
+    ``sender``
+        The comment model.
+        
+    ``comment``
+        The comment instance about to be posted. Note that it won't have been
+        saved into the database yet, so it won't have a primary key, and any
+        relations might not work correctly yet.
+        
+    ``request``
+        The :class:`~django.http.HttpRequest` that posted the comment.
+        
+comment_was_posted
+==================
+
+.. data:: django.contrib.comments.signals.comment_was_posted
+
+Sent just after the comment is saved.
+
+Arguments sent with this signal:
+    
+    ``sender``
+        The comment model.
+        
+    ``comment``
+        The comment instance that was posted. Note that it will have already
+        been saved, so if you modify it you'll need to call
+        :meth:`~django.db.models.Model.save` again.
+        
+    ``request``
+        The :class:`~django.http.HttpRequest` that posted the comment.
+
+comment_was_flagged
+===================
+
+Sent after a comment was "flagged" in some way. Check the flag to see if this
+was a user requesting removal of a comment, a moderator approving/removing a
+comment, or some other custom user flag.
+
+Arguments sent with this signal:
+    
+    ``sender``
+        The comment model.
+        
+    ``comment``
+        The comment instance that was posted. Note that it will have already
+        been saved, so if you modify it you'll need to call
+        :meth:`~django.db.models.Model.save` again.
+        
+    ``flag``
+        The :class:`~django.contrib.comments.models.CommentFlag` that's been
+        attached to the comment.
+        
+    ``created``
+        ``True`` if this is a new flag; ``False`` if it's a duplicate flag.
+        
+    ``request``
+        The :class:`~django.http.HttpRequest` that posted the comment.

+ 2 - 1
docs/ref/index.txt

@@ -9,13 +9,14 @@ API Reference
    contrib/index
    databases
    django-admin
+   files/index
    forms/index
    generic-views
    middleware
    models/index
    request-response
    settings
+   signals
    templates/index
    unicode
-   files/index
    

+ 294 - 0
docs/ref/signals.txt

@@ -0,0 +1,294 @@
+.. _ref-signals:
+
+=========================
+Built-in signal reference
+=========================
+
+A list of all the signals that Django sends.
+
+.. seealso::
+
+    The :ref:`comment framework <ref-contrib-comments-index>` sends a :ref:`set
+    of comment-related signals <ref-contrib-comments-signals>`.
+
+Model signals
+=============
+
+.. module:: django.db.models.signals
+   :synopsis: Signals sent by the model system.
+
+The :mod:`django.db.models.signals` module defines a set of signals sent by the
+module system.
+
+.. warning::
+
+    Many of these signals are sent by various model methods like
+    :meth:`~django.db.models.Model.__init__` or
+    :meth:`~django.db.models.Model.save` that you can overwrite in your own
+    code.
+
+    If you override these methods on your model, you must call the parent class'
+    methods for this signals to be sent.
+
+pre_init
+--------
+
+.. data:: django.db.models.signals.pre_init
+
+Whenever you instantiate a Django model,, this signal is sent at the beginning
+of the model's :meth:`~django.db.models.Model.__init__` method.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The model class that just had an instance created.
+
+    ``args``
+        A list of positional arguments passed to
+        :meth:`~django.db.models.Model.__init__`:
+
+    ``kwargs``
+        A dictionary of keyword arguments passed to
+        :meth:`~django.db.models.Model.__init__`:.
+
+For example, the :ref:`tutorial <intro-tutorial01>` has this line:
+
+.. code-block:: python
+
+    p = Poll(question="What's up?", pub_date=datetime.now())
+
+The arguments sent to a :data:`pre_init` handler would be:
+
+    ==========  ===============================================================
+    Argument    Value
+    ==========  ===============================================================
+    ``sender``  ``Poll`` (the class itself)
+
+    ``args``    ``[]`` (an empty list because there were no positional
+                arguments passed to ``__init__``.)
+
+    ``kwargs``  ``{'question': "What's up?", 'pub_date': datetime.now()}``
+    ==========  ===============================================================
+
+post_init
+---------
+
+.. data:: django.db.models.signals.post_init
+
+Like pre_init, but this one is sent when the :meth:`~django.db.models.Model.__init__`: method finishes.
+
+Arguments sent with this signal:
+
+    ``sender``
+        As above: rhe model class that just had an instance created.
+
+    ``instance``
+        The actual instance of the model that's just been created.
+
+pre_save
+--------
+
+.. data:: django.db.models.signals.pre_save
+
+This is sent at the beginning of a model's :meth:`~django.db.models.Model.save`
+method.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The model class.
+
+    ``instance``
+        The actual instance being saved.
+
+post_save
+---------
+
+.. data:: django.db.models.signals.post_save
+
+Like :data:`pre_save`, but sent at the end of the
+:meth:`~django.db.models.Model.save` method.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The model class.
+
+    ``instance``
+        The actual instance being saved.
+
+    ``created``
+        A boolean; ``True`` if a new record was create.
+
+pre_delete
+----------
+
+.. data:: django.db.models.signals.pre_delete
+
+Sent at the beginning of a model's :meth:`~django.db.models.Model.delete`
+method.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The model class.
+
+    ``instance``
+        The actual instance being saved.
+
+post_delete
+-----------
+
+.. data:: django.db.models.signals.post_delete
+
+Like :data:`pre_delete`, but sent at the end of the
+:meth:`~django.db.models.Model.delete` method.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The model class.
+
+    ``instance``
+        The actual instance being saved.
+
+        Note that the object will no longer be in the database, so be very
+        careful what you do with this instance
+
+class_prepared
+--------------
+
+.. data:: django.db.models.signals.class_prepared
+
+Sent whenever a model class has been "prepared" -- that is, once model has
+been defined and registered with Django's model system. Django uses this
+signal internally; it's not generally used in third-party applications.
+
+Arguments that are sent with this signal:
+
+``sender``
+    The model class which was just prepared.
+
+Management signals
+==================
+
+Signals sent by :ref:`django-admin <ref-django-admin>`.
+
+post_syncdb
+-----------
+
+.. data:: django.db.models.signals.post_syncdb
+
+Sent by :djadmin:`syncdb` after it installs an application.
+
+Any handlers that listen to this signal need to be written in a particular
+place: a ``management`` module in one of your :setting:`INSTALLED_APPS`. If
+handlers are registered anywhere else they may not be loaded by
+:djadmin:`syncdb`.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The ``models`` module that was just installed. That is, if
+        :djadmin:`syncdb` just installed an app called ``"foo.bar.myapp"``,
+        ``sender`` will be the ``foo.bar.myapp.models`` module.
+
+    ``app``
+        Same as ``sender``.
+
+    ``created_models``
+        A list of the model classes from any app which :djadmin:`syncdb` has
+        created so far.
+
+    ``verbosity``
+        Indicates how much information manage.py is printing on screen. See
+        the :djadminopt:`--verbosity`` flag for details.
+
+        Functions which listen for :data:`post_syncdb` should adjust what they
+        output to the screen based on the value of this argument.
+
+    ``interactive``
+        If ``interactive`` is ``True``, it's safe to prompt the user to input
+        things on the command line. If ``interactive`` is ``False``, functions
+        which listen for this signal should not try to prompt for anything.
+
+        For example, the :mod:`django.contrib.auth` app only prompts to create a
+        superuser when ``interactive`` is ``True``.
+
+Request/response signals
+========================
+
+.. module:: django.core.signals
+   :synopsis: Core signals sent by the request/response system.
+
+Signals sent by the core framework when processing a request.
+
+request_started
+---------------
+
+.. data:: django.core.signals.request_started
+
+Sent when Django begins processing an HTTP request.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The handler class -- i.e.
+        :class:`django.core.handlers.modpython.ModPythonHandler` or
+        :class:`django.core.handlers.wsgi.WsgiHandler` -- that handled
+        the request.
+
+request_finished
+----------------
+
+.. data:: django.core.signals.request_finished
+
+Sent when Django finishes processing an HTTP request.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The handler class, as above.
+
+got_request_exception
+---------------------
+
+.. data:: django.core.signals.got_request_exception
+
+This signal is sent whenever Django encounters an exception while processing an incoming HTTP request.
+
+Arguments sent with this signal:
+
+    ``sender``
+        The handler class, as above.
+
+    ``request``
+        The :class:`~django.http.HttpRequest` object.
+
+Test signals
+============
+
+.. module:: django.test.signals
+   :synopsis: Signals sent during testing.
+
+Signals only sent when :ref:`running tests <topics-testing>`.
+
+template_rendered
+-----------------
+
+.. data:: django.test.signals.template_rendered
+
+Sent when the test system renders a template. This signal is not emitted during
+normal operation of a Django server -- it is only available during testing.
+
+Arguments sent with this signal:
+
+    sender
+        The :class:`~django.template.Template` object which was rendered.
+
+    template
+        Same as sender
+
+    context
+        The :class:`~django.template.Context` with which the template was
+        rendered.

+ 2 - 1
docs/topics/index.txt

@@ -22,4 +22,5 @@ Introductions to all the key parts of Django you'll need to know:
    i18n
    pagination
    serialization
-   settings
+   settings
+   signals

+ 176 - 0
docs/topics/signals.txt

@@ -0,0 +1,176 @@
+.. _topics-signals:
+
+=======
+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 :ref:`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 is called.
+
+
+    * :data:`django.core.signals.request_started` &
+      :data:`django.core.signals.request_finished`
+
+      Sent when Django starts or finishes an HTTP request.
+
+See the :ref:`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. 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
+------------------
+
+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
+-----------------------------
+
+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 signals sent by specific senders
+----------------------------------------------
+
+Some signals get sent many times, but you'll only be interested in recieving 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 :ref:`built-in signal documentation <ref-signals>` for details of each
+particular signal.
+
+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
+---------------
+
+.. method:: Signal.send(sender, **kwargs)
+
+To send a signal, call :meth:`Signal.send`. 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)
+            ...
+
+