I didn’t know how to title this question, honestly. Suppose a view like this below. My users can have maximum 10 dollars in their pocket. I’ve added a button that hits this view, and adds 1 dollar at time until 10 is reached. This is just an example.

def my_view(request):
    money_to_add = 10 - request.user.money
    for i in range(0, money_to_add):
        user.money += 1
    request.user.save()
    return HttpResponse(“Ok!”)

Problem is that if the user clicks very fast the button two times, then ‘10 - request.user.money’ is added twice to the account.

How can i solve this problem, just modifying the view? I’d prefer avoid to use external packages or modify the model’s behavior.

Thank you.

EDIT: Using class based views is ok! I just used a fbv for example to make it clear.

1 Answers

1
AKX On

Just

user.money = min(10, user.money + 1)

will always clamp user.money to be 10 at most.

However, this won't take concurrency into account; you may bump into a race condition when something like this occurs:

REQUEST 1               REQUEST 2
load user (money=7)
user.money += 1         load user (money=7)
user.save()  (money=8)  user.money += 1
                        user.save()  (money=8)    <-- should be 9

For that, you'll have to use F() – the guide here talks about this case in particular, which will do the update atomically in the database:

user.money = Min(10, F('money') + 1)
user.refresh_from_db()  # if you need `user.money` to be the concrete value; otherwise it's an expression still