Race conditions in django Race conditions in django python python

Race conditions in django


Django 1.4+ supports select_for_update, in earlier versions you may execute raw SQL queries e.g. select ... for update which depending on underlying DB will lock the row from any updates, you can do whatever you want with that row until the end of transaction. e.g.

from django.db import transaction@transaction.commit_manually()def add_points(request):    user = User.objects.select_for_update().get(id=request.user.id)    # you can go back at this point if something is not right     if user.points > 1000:        # too many points        return    user.points += calculate_points(user)    user.save()    transaction.commit()


As of Django 1.1 you can use the ORM's F() expressions to solve this specific problem.

from django.db.models import Fuser = request.useruser.points  = F('points') + calculate_points(user)user.save()

For more details see the documentation:

https://docs.djangoproject.com/en/1.8/ref/models/instances/#updating-attributes-based-on-existing-fields

https://docs.djangoproject.com/en/1.8/ref/models/expressions/#django.db.models.F


Database locking is the way to go here. There are plans to add "select for update" support to Django (here), but for now the simplest would be to use raw SQL to UPDATE the user object before you start to calculate the score.


Pessimistic locking is now supported by Django 1.4's ORM when the underlying DB (such as Postgres) supports it. See the Django 1.4a1 release notes.