Overview
在本指南中,您可以学习;了解如何使用 Django MongoDB 后端来执行事务。事务允许您运行一系列仅在提交整个ACID 事务后才更改数据的操作。如果ACID 事务中的任何操作不成功,Django MongoDB后端会停止ACID 事务,并在数据更改变得可见之前丢弃所有更改。此功能称为原子性。
在MongoDB中,事务在逻辑会话中运行。会话是要按顺序运行的一组相关读取或写入操作。会话启用为一群组操作启用因果一致性,并允许您在符合ACID的ACID 事务中运行操作,该ACID 事务满足原子性、一致性、隔离性和持久性的预期。
不支持 Django 的原生ACID 事务API ,但 Django MongoDB后端提供了可实现相同功能的自定义ACID 事务API 。要在ACID 事务中运行操作,请在原子代码区块中定义这些操作。Django MongoDB后端在内部管理会话逻辑,因此您无需在运行ACID 事务之前手动启动会话。
样本数据
本指南中的示例使用 Movie
模型,该模型表示 Atlas示例数据集中的 sample_mflix.movies
集合。Movie
模型类具有以下定义:
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
Movie
模型包括一个内部 Meta
类(用于指定模型元数据)和一个 __str__()
方法(用于定义模型的字符串表示形式)。 要学习;了解这些模型功能,请参阅创建模型指南中的定义模型。
运行代码示例
您可以使用Python交互式Shell来运行代码示例。 要进入Shell ,请从项目的根目录运行以下命令:
python manage.py shell
进入Python Shell后,请确保导入以下模型和模块:
from <your application name>.models import Movie from django.utils import timezone from datetime import datetime
要学习;了解如何创建使用 Movie
模型和Python交互式Shell与MongoDB文档交互的Django应用程序,请访问入门教程。
启动事务
要启动数据库ACID 事务,请调用 django_mongodb_backend.transaction.atomic()
函数。您可以将此函数用作装饰器或上下文管理器。
确保使用以下导入声明导入 transaction
模块:
from django_mongodb_backend import transaction
使用装饰器
您可以通过在函数上方添加 @transaction.atomic
装饰器来启动数据库ACID 事务。此装饰器ACID 一致性保证函数内任何数据库操作的原子性。如果函数成功完成,则将更改提交到MongoDB。
以下示例在ACID 事务中调用 create()
方法,如果ACID 事务成功,该方法会将文档插入 sample_mflix.movies
集合:
def insert_movie_transaction(): Movie.objects.create( title="Poor Things", runtime=141, genres=["Comedy", "Romance"] )
使用上下文经理
或者,您可以使用 transaction.atomic()
上下文管理器创建原子代码区块。此示例运行与前面的示例相同的操作,但使用上下文管理器来启动ACID 事务:
def insert_movie_transaction(): with transaction.atomic(): Movie.objects.create( title="Poor Things", runtime=141, genres=["Comedy", "Romance"] )
在事务后运行回调
要仅在ACID 事务成功完成时才执行某些操作,可以使用 transaction.on_commit()
函数。此函数允许您注册在ACID 事务提交到数据库后运行的回调。将函数或任何可调用对象作为参数传递给 on_commit()
。
以下示例仅在相关数据库ACID 事务完成后查询 genre
值为 ["Horror", "Comedy"]
的电影:
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)
处理事务错误
要处理ACID 事务期间发生的异常,请在原子代码区块周围添加错误处理逻辑。如果在原子区块内处理错误,则可能会在 Django 中掩盖这些错误。由于 Django 使用错误来确定是提交还是回滚ACID 事务,因此这可能会导致意外行为。
如果ACID 事务不成功,Django 不会恢复对模型字段所做的任何更改。为了避免模型和数据库文档之间出现不一致,您可能需要手动恢复原始字段值。
例子
以下示例包含错误处理逻辑,该逻辑在数据库ACID 事务失败时恢复检索到的文档的修改后的 title
值:
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()
由于代码根据模型的 title
值执行第二个数据库操作,因此在ACID 事务错误时恢复更改可以防止进一步的数据不一致。