Django - 使用事务原子回滚保存
Posted
技术标签:
【中文标题】Django - 使用事务原子回滚保存【英文标题】:Django - Rollback save with transaction atomic 【发布时间】:2016-04-16 06:49:31 【问题描述】:我正在尝试创建一个保存对象的视图,但如果引发异常,我想 撤消 保存该对象。这是我尝试过的:
class MyView(View):
@transaction.atomic
def post(self, request, *args, **kwargs):
try:
some_object = SomeModel(...)
some_object.save()
if something:
raise exception.NotAcceptable()
# When the workflow comes into this condition, I think the previous save should be undone
# What am I missing?
except exception.NotAcceptable, e:
# do something
我做错了什么?即使引发异常some_object
仍在数据库中。
【问题讨论】:
【参考方案1】:Atomicity Documentation
总而言之,如果您的视图产生没有错误的响应,@transaction.atomic
将在数据库上执行事务。因为您自己捕获了异常,所以在 Django 看来,您的视图执行得很好。
如果捕获到异常,需要自己处理:Controlling Transactions
如果您需要在失败的情况下产生正确的 json 响应:
from django.db import SomeError, transaction
def viewfunc(request):
do_something()
try:
with transaction.atomic():
thing_that_might_fail()
except SomeError:
handle_exception()
render_response()
【讨论】:
此视图适用于 API,因此我认为我需要处理任何可能的错误以提供适当的 json 响应。有没有办法用原子装饰器做到这一点? 不使用装饰器恕我直言,因为它处理函数之外的事务。上下文管理器的好例子! 你必须在 try.. 中有一个原子块。除了答案中的块。如果你愿意,你也可以在视图上使用 atomic 装饰器。 实际上,您似乎不应该使用装饰器。您可能希望响应根据保存的成功而有所不同。因此,您应该像我在示例中那样做,然后您可以轻松判断您是否成功。如果响应不依赖于执行是否成功,则使用装饰器并且不要捕获/处理异常(删除 try/except 块)。 不,如果你想捕获异常,你必须在 try.. except 块中使用with transaction.atomic
。我的观点是,您可以同时使用@transaction.atomic
来查看视图。如果您希望 do_something()
和 try..except 块的结果在一个成功或失败的事务中,这仍然很有用。【参考方案2】:
但是,如果在一个用transaction.atomic修饰的函数中发生异常,那么你就没有什么可做的了,它会rollback automatically to the savepoint created by the decorator before running the your function,就像documented:
atomic 允许我们创建一个代码块,在其中保证数据库的原子性。如果代码块成功完成,则将更改提交到数据库。如果出现异常,则回滚更改。
如果异常在 except 块中被捕获,那么应该重新引发它以供 atomic 捕获并执行回滚,即:
try:
some_object = SomeModel(...)
some_object.save()
if something:
raise exception.NotAcceptable()
# When the workflow comes into this condition, I think the previous save should be undome
# Whant am I missing?
except exception.NotAcceptable, e:
# do something
raise # re-raise the exception to make transaction.atomic rollback
另外,如果你想要更多的控制权,你可以手动回滚到previously set savepoint,即:
class MyView(View):
def post(self, request, *args, **kwargs):
sid = transaction.savepoint()
some_object = SomeModel(...)
some_object.save()
if something:
transaction.savepoint_rollback(sid)
else:
try:
# In worst case scenario, this might fail too
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
【讨论】:
这就是我的想法,这就是我以这种方式执行此功能的原因,但正如我在问题中所说,当引发异常时,对象仍在数据库事件中,是否有任何附加步骤使用保存点? 可能是因为异常被 except 块捕获,并没有重新引发,所以 atomic 认为它是成功执行的函数。 如果我没有发现错误,我无法给出好的回应。如何构建我的函数? 添加了保存点回滚的示例,但我认为上下文管理器在这种情况下可能更合适。 使用带有 try catch 的保存点不会在失败的情况下回滚任何事务。谁能告诉我在下面的链接中使用带有 try-catch 的保存点:***.com/questions/60314464/…【参考方案3】:对我来说,这适用于 Django 2.2.5
首先在你的settings.py中
...
DATABASES =
'default':
'ENGINE': 'xxx', # transactional db
...
'ATOMIC_REQUESTS': True,
在你的函数中(views.py)
from django.db import transaction
@transaction.atomic
def make_db_stuff():
# do stuff in your db (inserts or whatever)
if success:
return True
else:
transaction.set_rollback(True)
return False
【讨论】:
将ATOMIC_REQUESTS
设置为 True
会使您的 all 视图具有原子性 - 因此您应该将其设置为 true 并在要排除的视图上使用 @transaction.non_atomic_requests
,或者只是通过@transaction.atomic
单独将视图设置为原子视图。来源:docs.djangoproject.com/en/3.0/topics/db/transactions/…以上是关于Django - 使用事务原子回滚保存的主要内容,如果未能解决你的问题,请参考以下文章