Browse Source

Refs #33939 -- Improved transaction.on_commit() docs.

Adam Johnson 2 years ago
parent
commit
444b6da7cc
1 changed files with 27 additions and 25 deletions
  1. 27 25
      docs/topics/db/transactions.txt

+ 27 - 25
docs/topics/db/transactions.txt

@@ -290,45 +290,47 @@ Performing actions after commit
 
 
 Sometimes you need to perform an action related to the current database
 Sometimes you need to perform an action related to the current database
 transaction, but only if the transaction successfully commits. Examples might
 transaction, but only if the transaction successfully commits. Examples might
-include a `Celery`_ task, an email notification, or a cache invalidation.
+include a background task, an email notification, or a cache invalidation.
 
 
-.. _Celery: https://pypi.org/project/celery/
-
-Django provides the :func:`on_commit` function to register callback functions
-that should be executed after a transaction is successfully committed:
+:func:`on_commit` allows you to register callbacks that will be executed after
+the open transaction is successfully committed:
 
 
 .. function:: on_commit(func, using=None, robust=False)
 .. function:: on_commit(func, using=None, robust=False)
 
 
-Pass any function (that takes no arguments) to :func:`on_commit`::
+Pass a function, or any callable, to :func:`on_commit`::
 
 
     from django.db import transaction
     from django.db import transaction
 
 
-    def do_something():
-        pass  # send a mail, invalidate a cache, fire off a Celery task, etc.
+    def send_welcome_email():
+        ...
 
 
-    transaction.on_commit(do_something)
+    transaction.on_commit(send_welcome_email)
 
 
-You can also bind arguments to your function using :func:`functools.partial`::
+Callbacks will not be passed any arguments, but you can bind them with
+:func:`functools.partial`::
 
 
     from functools import partial
     from functools import partial
 
 
-    transaction.on_commit(partial(some_celery_task.delay, 'arg1'))
+    for user in users:
+        transaction.on_commit(
+            partial(send_invite_email, user=user)
+        )
 
 
-The function you pass in will be called immediately after a hypothetical
-database write made where ``on_commit()`` is called would be successfully
-committed.
+Callbacks are called after the open transaction is successfully committed. If
+the transaction is instead rolled back (typically when an unhandled exception
+is raised in an :func:`atomic` block), the callback will be discarded, and
+never called.
 
 
-If you call ``on_commit()`` while there isn't an active transaction, the
-callback will be executed immediately.
+If you call ``on_commit()`` while there isn't an open transaction,
+the callback will be executed immediately.
 
 
-If that hypothetical database write is instead rolled back (typically when an
-unhandled exception is raised in an :func:`atomic` block), your function will
-be discarded and never called.
+It's sometimes useful to register callbacks that can fail. Passing
+``robust=True`` allows the next callbacks to be executed even if the current
+one throws an exception. All errors derived from Python's ``Exception`` class
+are caught and logged to the ``django.db.backends.base`` logger.
 
 
-It's sometimes useful to register callback functions that can fail. Passing
-``robust=True`` allows the next functions to be executed even if the current
-function throws an exception. All errors derived from Python's ``Exception``
-class are caught and logged to the ``django.db.backends.base`` logger.
+You can use :meth:`.TestCase.captureOnCommitCallbacks` to test callbacks
+registered with :func:`on_commit`.
 
 
 .. versionchanged:: 4.2
 .. versionchanged:: 4.2
 
 
@@ -390,8 +392,8 @@ Timing of execution
 Your callbacks are executed *after* a successful commit, so a failure in a
 Your callbacks are executed *after* a successful commit, so a failure in a
 callback will not cause the transaction to roll back. They are executed
 callback will not cause the transaction to roll back. They are executed
 conditionally upon the success of the transaction, but they are not *part* of
 conditionally upon the success of the transaction, but they are not *part* of
-the transaction. For the intended use cases (mail notifications, Celery tasks,
-etc.), this should be fine. If it's not (if your follow-up action is so
+the transaction. For the intended use cases (mail notifications, background
+tasks, etc.), this should be fine. If it's not (if your follow-up action is so
 critical that its failure should mean the failure of the transaction itself),
 critical that its failure should mean the failure of the transaction itself),
 then you don't want to use the :func:`on_commit` hook. Instead, you may want
 then you don't want to use the :func:`on_commit` hook. Instead, you may want
 `two-phase commit`_ such as the :ref:`psycopg Two-Phase Commit protocol support
 `two-phase commit`_ such as the :ref:`psycopg Two-Phase Commit protocol support