Django: избегать условия гонки для поля, значение которого основано на агрегации - PullRequest
0 голосов
/ 06 февраля 2019

Существует пример модели:

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 голосов
/ 06 февраля 2019

Я думаю, что простая атомарная транзакция должна решить вашу проблему.

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при создании записи (сохранение в первый раз).Вы можете сделать это, убедившись, что pk равен None следующим образом.

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)
...