【发布时间】:2011-04-01 04:07:06
【问题描述】:
在模型的save() 方法中应该如何处理可能的竞争条件?
例如,以下示例实现了一个具有相关项目有序列表的模型。创建新项目时,当前列表大小用作其位置。
据我所知,如果同时创建多个项目,这可能会出错。
class OrderedList(models.Model):
# ....
@property
def item_count(self):
return self.item_set.count()
class Item(models.Model):
# ...
name = models.CharField(max_length=100)
parent = models.ForeignKey(OrderedList)
position = models.IntegerField()
class Meta:
unique_together = (('parent','position'), ('parent', 'name'))
def save(self, *args, **kwargs):
if not self.id:
# use item count as next position number
self.position = parent.item_count
super(Item, self).save(*args, **kwargs)
我遇到过@transactions.commit_on_success(),但这似乎只适用于视图。即使它确实适用于模型方法,我仍然不知道如何正确处理失败的事务。
我目前正在这样处理它,但感觉更像是一种 hack 而不是解决方案
def save(self, *args, **kwargs):
while not self.id:
try:
self.position = self.parent.item_count
super(Item, self).save(*args, **kwargs)
except IntegrityError:
# chill out, then try again
time.sleep(0.5)
有什么建议吗?
更新:
上述解决方案的另一个问题是,如果IntegrityError 是由name 冲突(或与此相关的任何其他唯一字段)引起的,while 循环将永远不会结束。
为了记录,这是我目前所拥有的,似乎可以满足我的需要:
def save(self, *args, **kwargs):
# for object update, do the usual save
if self.id:
super(Step, self).save(*args, **kwargs)
return
# for object creation, assign a unique position
while not self.id:
try:
self.position = self.parent.item_count
super(Step, self).save(*args, **kwargs)
except IntegrityError:
try:
rival = self.parent.item_set.get(position=self.position)
except ObjectDoesNotExist: # not a conflict on "position"
raise IntegrityError
else:
sleep(random.uniform(0.5, 1)) # chill out, then try again
【问题讨论】:
标签: python database django django-models race-condition