-
Notifications
You must be signed in to change notification settings - Fork 7
DOCSP-51036: Transactions #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
073c271
07a82ac
f1560c9
005217f
9652563
48f877e
fab0362
7b36ded
4d0fdb3
90d24a8
72b93db
d1c6872
0d93cbb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from django.db import transaction, DatabaseError | ||
from sample_mflix.models import Movie | ||
|
||
# start-transaction-decorator | ||
@transaction.atomic | ||
def insert_movie_transaction(): | ||
Movie.objects.create( | ||
title="Poor Things", | ||
runtime=141, | ||
genres=["Comedy", "Romance"] | ||
) | ||
# end-transaction-decorator | ||
|
||
# start-transaction-manager | ||
def insert_movie_transaction(): | ||
with transaction.atomic(): | ||
Movie.objects.create( | ||
title="Poor Things", | ||
runtime=141, | ||
genres=["Comedy", "Romance"] | ||
) | ||
# end-transaction-manager | ||
|
||
# start-callback | ||
def get_horror_comedies(): | ||
movies = Movie.objects.filter(genres=["Horror", "Comedy"]) | ||
for m in movies: | ||
print(f"Title: {m.title}, runtime: {m.runtime}") | ||
|
||
def insert_movie_with_callback(): | ||
with transaction.atomic(): | ||
Movie.objects.create( | ||
title="The Substance", | ||
runtime=140, | ||
genres=["Horror", "Comedy"] | ||
) | ||
|
||
transaction.on_commit(get_horror_comedies) | ||
# end-callback | ||
|
||
# start-handle-errors | ||
movie = Movie.objects.get( | ||
title="Jurassic Park", | ||
released=timezone.make_aware(datetime(1993, 6, 11)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably enough to retrieve a move by name (omit |
||
) | ||
try: | ||
with transaction.atomic(): | ||
movie.update(title="Jurassic Park I") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. models don't have an |
||
except DatabaseError: | ||
movie.update(title="Jurassic Park") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might copy from Django's example and use the value of |
||
# end-handle-errors |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,151 @@ | ||||||
.. _django-transactions: | ||||||
|
||||||
========================= | ||||||
Transactions and Sessions | ||||||
========================= | ||||||
|
||||||
.. facet:: | ||||||
:name: genre | ||||||
:values: reference | ||||||
|
||||||
.. meta:: | ||||||
:keywords: code example, ACID compliance, multi-document | ||||||
|
||||||
.. contents:: On this page | ||||||
:local: | ||||||
:backlinks: none | ||||||
:depth: 2 | ||||||
:class: singlecol | ||||||
|
||||||
Overview | ||||||
-------- | ||||||
|
||||||
In this guide, you can learn how to use {+django-odm+} to perform | ||||||
**transactions**. Transactions allow you to run a series of operations | ||||||
that change data only if the entire transaction is committed. | ||||||
If any operation in the transaction does not succeed, {+django-odm+} stops the | ||||||
transaction and discards all data changes before they ever become | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Consider this for clarity |
||||||
visible. This feature is called **atomicity**. | ||||||
|
||||||
In MongoDB, transactions run within logical sessions. A | ||||||
session is a grouping of related read or write operations that you | ||||||
want to run sequentially. Sessions enable causal consistency for a group | ||||||
of operations and allow you to run operations in an **ACID-compliant** | ||||||
transaction, which is a transaction that meets an expectation of | ||||||
atomicity, consistency, isolation, and durability. | ||||||
|
||||||
You can use {+framework+}'s transaction API to perform database transactions. | ||||||
To run operations within a transaction, define them inside an atomic block of | ||||||
code. {+framework+} manages session logic internally, so you do not need to | ||||||
manually start a session before running a transaction. | ||||||
|
||||||
.. important:: Transaction Limitations | ||||||
|
||||||
{+django-odm+}'s support for the {+framework+} transaction API | ||||||
has several limitations. To view a list of limitations, see | ||||||
:ref:`Database and Collection Support <django-feature-compat-db-coll>` | ||||||
in the Django and MongoDB Feature Compatibility guide. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Sample Data | ||||||
~~~~~~~~~~~ | ||||||
|
||||||
The examples in this guide use the ``Movie`` model, which represents | ||||||
the ``sample_mflix.movies`` collection from the :atlas:`Atlas sample datasets </sample-data>`. | ||||||
The ``Movie`` model class has the following definition: | ||||||
|
||||||
.. literalinclude:: /includes/interact-data/crud.py | ||||||
:start-after: start-models | ||||||
:end-before: end-models | ||||||
:language: python | ||||||
:copyable: | ||||||
|
||||||
.. include:: /includes/use-sample-data.rst | ||||||
|
||||||
.. replacement:: model-classes | ||||||
|
||||||
``Movie`` model includes | ||||||
|
||||||
.. replacement:: model-imports | ||||||
|
||||||
.. code-block:: python | ||||||
|
||||||
from <your application name>.models import Movie | ||||||
from django.utils import timezone | ||||||
from datetime import datetime | ||||||
|
||||||
Start a Transaction | ||||||
------------------- | ||||||
|
||||||
To start a database transaction, define an atomic block of code | ||||||
by adding the ``@transaction.atomic`` decorator above your function. | ||||||
This decorator guarantees the atomicity of any database operations | ||||||
within the function. If the function successfully completes, the | ||||||
changes are committed MongoDB. | ||||||
|
||||||
The following example calls the ``create()`` method within a transaction, | ||||||
inserting a document into the ``sample_mflix.movies`` collection if the | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
transaction succeeds: | ||||||
|
||||||
.. literalinclude:: /includes/interact-data/transactions.py | ||||||
:start-after: start-transaction-decorator | ||||||
:end-before: end-transaction-decorator | ||||||
:language: python | ||||||
:copyable: | ||||||
|
||||||
Alternatively, you can use the ``transaction.atomic()`` context manager | ||||||
to create an atomic block. This example runs the same operation as the | ||||||
preceding example but uses a context manager to start a transaction: | ||||||
|
||||||
.. literalinclude:: /includes/interact-data/transactions.py | ||||||
:start-after: start-transaction-manager | ||||||
:end-before: end-transaction-manager | ||||||
:language: python | ||||||
:copyable: | ||||||
|
||||||
Run Callbacks After a Transaction | ||||||
--------------------------------- | ||||||
|
||||||
If you want to perform certain actions only if a transaction successfully | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
completes, you can use the ``transaction.on_commit()`` method. This method allows you to | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
register callbacks that run after a transaction is committed to the | ||||||
database. Pass a function, or any callable object, as an argument to | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think this is what this says There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll keep as is to avoid using too many commas, since I think the "to use..." is implied |
||||||
``on_commit()``. | ||||||
|
||||||
The following example queries for movies that have a ``genre`` value of | ||||||
``["Horror", "Comedy"]`` only after a related database transaction completes: | ||||||
|
||||||
.. literalinclude:: /includes/interact-data/transactions.py | ||||||
:start-after: start-callback | ||||||
:end-before: end-callback | ||||||
:language: python | ||||||
:copyable: | ||||||
|
||||||
Handle Transaction Errors | ||||||
------------------------- | ||||||
|
||||||
To handle exceptions that occur during a transaction, add error handling | ||||||
logic around your atomic code block. If you include error handing logic inside | ||||||
the atomic block, {+framework+} might ignore these errors, resulting in unexpected | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the meaning of "{+framework+} might ignore these errors," is probably unclear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some more detail here |
||||||
behavior. | ||||||
|
||||||
If a transaction does not succeed, your application does not revert any changes made | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "your application" -> Django |
||||||
to a model's fields. To avoid inconsistencies between your models and database documents, | ||||||
you might need to manually restore the original field values. | ||||||
|
||||||
Example | ||||||
~~~~~~~ | ||||||
|
||||||
The following example includes error handling logic that reverts the modified | ||||||
``title`` value of the retrieved document if the database transaction fails: | ||||||
|
||||||
.. literalinclude:: /includes/interact-data/transactions.py | ||||||
:start-after: start-handle-errors | ||||||
:end-before: end-handle-errors | ||||||
:language: python | ||||||
:copyable: | ||||||
|
||||||
Additional Information | ||||||
---------------------- | ||||||
|
||||||
To learn more about the {+framework+} transaction API, see `Database Transactions | ||||||
<{+django-docs+}/topics/db/transactions>`__ in the {+framework+} documentation. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,6 +183,8 @@ Query Support | |
the :ref:`raw_aggregate() method <django-raw-queries-search>`. | ||
- ✓ | ||
|
||
.. _django-feature-compat-db-coll: | ||
|
||
Database and Collection Support | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
|
@@ -248,8 +250,17 @@ Database and Collection Support | |
- ✓ | ||
|
||
* - Transactions | ||
- *Unsupported*. | ||
- ✓ | ||
- *Partially Supported*. You can use {+framework+}'s transactions API with the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not an issue for this PR to tackle, but I think it's a strange choice of words to call it "Partial support" when there are any limitations. To me, "partial support" makes me think "this feature is partially completed and more work will be done later". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm I see what you mean, is "Supported with Limitations" more accurate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but I think it would be enough to say, "✓ You use... with the following limitations:" rather than place a double emphasis on limitations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I like ✓ with limitations noted. Partial support has more of a "we may get further on this soon" which I don't think is the case with transactions support. |
||
following limitations: | ||
|
||
- ``QuerySet.union()`` is not supported within a transaction. | ||
- If a transaction generates an error, the transaction is no longer usable. | ||
- Savepoints, or nested atomic blocks, are not supported. The outermost atomic block starts | ||
a transaction, and any subsequent atomic blocks have no effect. | ||
- Migration operations do not run inside a transaction, which differs from {+framework+}'s | ||
default migration behavior. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This isn't quite accurate. It depends on the database. SQLite & PostgreSQL run operations inside a transaction, but MySQL and Oracle don't. (The relevant feature flag is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed this part |
||
- Your MongoDB deployment must be a replica set or sharded cluster. | ||
- *Partially Supported*. | ||
|
||
Django Features | ||
--------------- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chop the blank line?