我有一个定义了字段的Django模型:
contacts = models.PositiveIntegerField(default=0)
...
再往下我试图使用F()
表达式对字段进行减量:
self.contacts = models.F("contacts") - quantity
如果不引入竞争条件,我如何检查contacts - quantity
是否为负值?
答案 0 :(得分:1)
你想避免竞争条件。 F()
个对象只是其中的第一步。
您在交易中要做的第一件事就是获取您正在更改的行的锁定。这可以防止您在写入数据库时读取过时的值。这可以通过更新行来完成,这将阻止行进一步更新,直到提交或回滚事务为止:
with transaction.atomic():
obj.contacts = F('contacts') - quantity
当您拥有锁定并完成更新时,请检查数据完整性是否仍然完好(即联系人数量不低于0)。如果是,则继续,否则,通过引发异常来回滚事务:
obj.refresh_from_db()
if obj.contacts < 0:
raise ValueError("Capacity exceeded")
如果剩下足够的联系人,此时您将退出atomic()
块,提交事务,其他请求可以获取锁并尝试更新该值。如果没有足够的联系人,事务将被回滚,其他请求永远不会知道值已经改变,因为他们一直在等待锁定。
现在,把它们放在一起:
from django.db import transaction
from django.db.models import F
def update_contacts(obj, quantity):
with transaction.atomic():
obj.contacts = F('contacts') - quantity
obj.save()
obj.refresh_from_db()
if obj.contacts < 0:
raise ValueError("Not enough contacts.")
(注意:obj.refresh_from_db()
需要1.8,否则只需使用MyModel.objects.get(pk=obj.pk)
。)