我有一个领域的Django模型定义:检查约束
contacts = models.PositiveIntegerField(default=0)
... 再往下我试图做使用F()
表达式领域的减量:
self.contacts = models.F("contacts") - quantity
如何查看contacts - quantity
不引入竞争条件不变成负?
我有一个领域的Django模型定义:检查约束
contacts = models.PositiveIntegerField(default=0)
... 再往下我试图做使用F()
表达式领域的减量:
self.contacts = models.F("contacts") - quantity
如何查看contacts - quantity
不引入竞争条件不变成负?
你想避免竞争条件。对象只是这方面的第一步。
您想要在您的交易中执行的第一件事是在您正在更改的行上获取锁。这可以防止您读取写入数据库时过时的值。这与该行的更新,直到事务被提交或回滚,将锁从进一步更新该行来完成:
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)
)
AFAIK没有定义这种约束的方式,但你可以做交易中的支票('@ transaction.atomic')。还有[此库](https://code.google.com/p/django-check-constraints/wiki/Features),但它确实很旧。 – Ivan
@伊万,谢谢你。如果我使用'@ transaction.atomic'装饰器,那么我不需要使用'F()'表达式,对吧? –
您将已经检索到所有内容,因此不会。 – Ivan