Django:避免基于聚集的字段的竞争条件

时间:2019-02-06 14:07:17

标签: django race-condition

有示例模型:

class MyModel(models.Model):
    name = models.CharField()
    version = models.IntegerField()

我需要根据具有相同名称的实例的最高版本来设置version字段值。为此,我重写了save方法:

   def save(self, *args, **kwargs):
       max_version = MyModel.objects \
           .filter(name=self.name) \
           .aggregate(max_version=Max('version'))['max_version'] or 0
       self.version = max_version + 1
       super(MyModel, self).save(*args, **kwargs)

如何在这里避免比赛情况?

编辑:版本必须唯一(在具有相同名称的实例之间),但顺序不是太重要。

1 个答案:

答案 0 :(得分:0)

我认为一个简单的原子transaction应该可以解决您的问题。

from django.db import transaction


def save(self, *args, **kwargs):
    with transaction.atomic():
        max_version = MyModel.objects \
           .filter(name=self.name) \
           .aggregate(max_version=Max('version'))['max_version'] or 0
        self.version = max_version + 1
        super(MyModel, self).save(*args, **kwargs)

尽管超出实际问题的范围,您可能只想在创建记录时才更新version字段(首次保存)。您可以通过验证pkNone来做到这一点。

from django.db import transaction


def save(self, *args, **kwargs):
    if self.pk is None:
        with transaction.atomic():
            max_version = MyModel.objects \
               .filter(name=self.name) \
               .aggregate(max_version=Max('version'))['max_version'] or 0
            self.version = max_version + 1
            super(MyModel, self).save(*args, **kwargs)

    else:
        super(MyModel, self).save(*args, **kwargs)