Django:避免数据库和ORM中的ABA场景

时间:2011-12-02 07:50:21

标签: database django algorithm

我有一种情况需要更新候选人的投票。

公民可以为这位候选人投票,每名候选人获得多于一票。即一个人可以投票5票,而另一个人投票2.在这种情况下,这个候选人应该得到7票。

现在,我使用 Django 。这里伪代码看起来像

votes = candidate.votes 
vote += citizen.vote 

这里的问题,正如你所看到的那样是一个 竞争条件 ,其中候选人的投票可能会被另一个公民的投票覆盖,他们之前选择了一个并立即设置。

如何使用像 这样的 ORM来避免这种情况?

2 个答案:

答案 0 :(得分:2)

如果这只是一个算术表达式,那么Django有一个很好的API叫F expressions


根据现有字段更新属性

有时您需要在字段上执行简单的算术任务,例如递增或递减当前值。实现这一目标的显而易见的方法是执行以下操作:

>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

如果从数据库中检索到的旧number_sold值为10,那么值11将被写回数据库。

可以通过相对于原始字段值表示更新来略微优化,而不是作为新值的显式赋值。 Django提供F()表达式作为执行此类相对更新的方式。使用F()表达式,前面的示例将表示为:

>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()

此方法不使用数据库中的初始值。相反,它使数据库根据执行save()时的当前值进行更新。

保存对象后,必须重新加载对象才能访问应用于更新字段的实际值:

>>> product = Products.objects.get(pk=product.pk)
>>> print product.number_sold
42

答案 1 :(得分:0)

也许select_for_update QuerySet方法对您有所帮助。

文档的摘录:

  

所有匹配的条目将被锁定,直到事务块结束,这意味着将阻止其他事务更改或获取锁定。

     

通常,如果另一个事务已经对其中一个选定行获取了锁定,则查询将一直阻塞,直到锁定被释放。如果这不是您想要的行为,请调用select_for_update(nowait = True)。这将使呼叫无阻塞。如果另一个事务已经获取了冲突的锁,则在评估查询集时将引发DatabaseError。

请注意,这仅适用于Django开发版本(即> 1.3)。