Как блокировать строки при операциях чтения и записи?
У меня есть модель, которая подсчитывает, сколько операций было сделано пользователем. (Она упрощена для примера)
User = get_user_model()
class UserOperationCounter(models.Model):
count = models.IntegerField(default=0)
user = models.OneToOne(User)
У меня есть контекстный менеджер, который получает объект счетчика для пользователя и увеличивает параметр counter
после выхода
@atomic()
class IncrementCounterContextManager:
def __init__(user_id):
self.operation_counter = UserOperationCounter.objects.filter(user_id=user_id).select_for_update().get()
def __enter__():
print(f"Fetch counter={operation_counter.counter} for user={operation_counter.user.id}")
return self.operation_counter
def __exit__():
self.operation_counter.count = F("count") + 1
self.operation_counter.save(update_fields=["count"])
self.operation_counter.refresh_from_db()
print(f"Set counter={operation_counter.counter} for user={operation_counter.user.id}")
Допустим, я хочу использовать контекстный менеджер каждый раз, когда пользователь выполняет какое-то определенное действие на платформе, а затем я хочу отправить ему уведомление с сообщением: "You have done <counter> hidden actions on our platform"
.
select_for_update()
прекрасно работает в сценариях, когда я хочу одновременно обновить объект Counter, но строка не заблокирована для чтения.
Вот пример:
def send_notification(counter, user_id):
print(f"Send notification with counter={counter} to user={user_id}")
user_id = 1
with atomic():
with IncrementCounterContextManager(user_id=1) as usr_counter1:
send_notification(usr_counter1.counter, usr_counter1.user.id)
with IncrementCounterContextManager(user_id=1) as usr_counter2:
send_notification(usr_counter2.counter, usr_counter2.user.id)
Когда я проверяю журналы, я вижу что-то вроде:
Fetch counter=0 for user=1
Fetch counter=0 for user=1
Send notification with counter=0 to user=1
Send notification with counter=0 to user=1
Set counter=1 for user=1
Set counter=2 for user=1
Я хотел бы узнать, как я могу заблокировать строку для чтения, чтобы получить только последнее значение после оценки объекта? Может быть, я могу как-то узнать, что объект нужно обновить?