Django get_or_create then update race condition

У меня следующая модель:

class UserProductBinding:
    product = models.ForeignKey(Product)
    user = models.ForeignKey(User)
    purchased = models.BooleanField(default=False)
    liked = models.BooleanField(default=False)

class Meta(object):
    unique_together = ['product', 'user']

Теперь мне нужна функция, которая делает покупку продукта пользователем, которая должна

  1. Get UserProductBinding with corresponsing user and product, or create one if it doesn't exist
  2. Make sure "purchased" field is False
  3. Set "purchased" to True and deduct money from user's balance

Я делаю это с помощью следующего кода:

binding, created = UserProductBinding.objects.get_or_create(product=product, user=user)
if binding.purchased:
   raise Error()
with transaction.atomic():
   binding.purchased = True
   user.money -= price
   binding.save()
   user.save()

Проблема в том, что у меня в этом коде есть условие гонки. Если эта функция будет вызвана дважды в разных потоках в течение короткого промежутка времени, есть вероятность, что проверка "if binding.purchased:" вернет False для обоих потоков, следовательно, функция выполнит свою работу дважды и деньги пользователя будут списаны дважды.

select_for_update у меня не работает, потому что мне нужно создать привязку, если она не существует. update_or_create тоже не работает, потому что он не проверяет, что поле "purchased" равно false, прежде чем сделать обновление.

Похоже, что использование get_or_create с последующим select_for_update поможет, но это требует дополнительных ударов по базе данных.

Я мог бы создать свою собственную функцию create_or_select_for_update, которая не кажется очень сложной, но тот факт, что такая функция отсутствует в Django, заставляет меня думать, что я, возможно, иду в неправильном направлении и, возможно, упускаю что-то очевидное.

Заранее спасибо!

Вернуться на верх