在Django中有条件更新而没有竞争条件

Jaa*_*nen 2 django transactions

如何根据模型字段的当前值更新模型字段并避免出现竞争状况?更新任务可以写为:

if (self.x == y):
    self.x = z
    self.save()
else:
    raise Exception()
Run Code Online (Sandbox Code Playgroud)

但是,存在竞争条件。我想出了以下解决方案:

from django.db import transaction
with transaction.atomic():
    if (self.x == y):
        self.x = z
        self.save()
    else:
        raise Exception()
Run Code Online (Sandbox Code Playgroud)

但这安全吗,还有更好的方法吗?

Kev*_*nry 5

不,该atomic()块不会做任何事情,因为self在尝试运行事务之前,您已经从数据库中获取了值,并已将这些值获取到其中。

如果您可以在查询参数中表达条件,则可以使用update()以下命令在单个查询中安全地执行此操作:

num_matched = MyModel.objects.filter(id=self.id, x=y).update(x=z)
if num_matched != 1:
    raise Exception()
Run Code Online (Sandbox Code Playgroud)

如果没有,您可以使用select_for_update()

with transaction.atomic():
    current = MyModel.objects.select_for_update().get(id=self.id)
    if (current.x == y):
        current.x = z
        current.save()
    else:
        raise Exception()
Run Code Online (Sandbox Code Playgroud)

此代码与上面的代码之间的区别在于,在进行比较之前,您要在此明确告诉数据库要锁定的行。

  • @jluttine:要真正理解这一点,您必须阅读有关数据库事务的信息。但简单来说:1)get()和save()是事务中需要进行的两个不同的操作;根据定义,自动提交将单个操作置于事务中。2)“ atomic()”上下文管理器将通过告诉数据库回滚事务(释放锁)来响应引发的异常。 (3认同)