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']
Теперь мне нужна функция, которая делает покупку продукта пользователем, которая должна
- Get UserProductBinding with corresponsing user and product, or create one if it doesn't exist
- Make sure "purchased" field is False
- 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, заставляет меня думать, что я, возможно, иду в неправильном направлении и, возможно, упускаю что-то очевидное.
Заранее спасибо!