Docs Menu
Docs Home
/ / /
Django MongoDB Backend
/

Transactions and Sessions

In this guide, you can learn how to use Django MongoDB Backend 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 MongoDB Backend stops the transaction and discards all changes to the data before they ever become 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 Django's transaction API to perform database transactions. To run operations within a transaction, define them inside an atomic block of code. Django manages session logic internally, so you do not need to manually start a session before running a transaction.

Important

Transaction Limitations

Django MongoDB Backend's support for the Django transaction API has several limitations. To view a list of limitations, see Database and Collection Support in the Django and MongoDB Feature Compatibility guide.

The examples in this guide use the Movie model, which represents the sample_mflix.movies collection from the Atlas sample datasets. The Movie model class has the following definition:

from django.db import models
from django_mongodb_backend.fields import ArrayField
class Movie(models.Model):
title = models.CharField(max_length=200)
plot = models.TextField(blank=True)
runtime = models.IntegerField(default=0)
released = models.DateTimeField("release date", null=True, blank=True)
genres = ArrayField(models.CharField(max_length=100), null=True, blank=True)
class Meta:
db_table = "movies"
managed = False
def __str__(self):
return self.title

The Movie model includes an inner Meta class, which specifies model metadata, and a __str__() method, which defines the model's string representation. To learn about these model features, see Define a Model in the Create Models guide.

You can use the Python interactive shell to run the code examples. To enter the shell, run the following command from your project's root directory:

python manage.py shell

After entering the Python shell, ensure that you import the following models and modules:

from <your application name>.models import Movie
from django.utils import timezone
from datetime import datetime

To learn how to create a Django application that uses the Movie model and the Python interactive shell to interact with MongoDB documents, visit the Get Started with Django MongoDB Backend tutorial.

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 to MongoDB.

The following example calls the create() method within a transaction, which inserts a document into the sample_mflix.movies collection if the transaction succeeds:

@transaction.atomic
def insert_movie_transaction():
Movie.objects.create(
title="Poor Things",
runtime=141,
genres=["Comedy", "Romance"]
)

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:

def insert_movie_transaction():
with transaction.atomic():
Movie.objects.create(
title="Poor Things",
runtime=141,
genres=["Comedy", "Romance"]
)

To perform certain actions only if a transaction successfully completes, you can use the transaction.on_commit() function. This function allows you to register callbacks that run after a transaction is committed to the database. Pass a function, or any callable object, as an argument to on_commit().

The following example queries for movies that have a genre value of ["Horror", "Comedy"] only after a related database transaction completes:

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)

To handle exceptions that occur during a transaction, add error handling logic around your atomic code block. If you handle errors inside the atomic block, you might obscure these errors from Django. Since Django uses errors to determine whether to commit or roll back a transaction, this can cause unexpected behavior.

If a transaction does not succeed, Django does not revert any changes made to a model's fields. To avoid inconsistencies between your models and database documents, you might need to manually restore the original field values.

The following example includes error handling logic that reverts the modified title value of the retrieved document if the database transaction fails:

movie = Movie.objects.get(title="Jurassic Park")
movie.title = "Jurassic Park I"
try:
with transaction.atomic():
movie.save()
except DatabaseError:
movie.title = "Jurassic Park"
if movie.title == "Jurassic Park I":
movie.plot = "An industrialist invites experts to visit his theme park of cloned dinosaurs. After a power failure," \
" the creatures run loose, putting everyone's lives, including his grandchildren's, in danger."
movie.save()

Since the code performs a second database operation based on the model's title value, reverting the change if the transaction errors prevents further data inconsistencies.

To learn more about the Django transaction API, see Database Transactions in the Django documentation.

Back

Perform Raw Queries

On this page