Django: How can I protect against concurrent modification of database entries

If there a way to protect against concurrent modifications of the same data base entry by two or more users?

It would be acceptable to show an error message to the user performing the second commit/save operation, but data should not be silently overwritten.

I think locking the entry is not an option, as a user might use the "Back" button or simply close his browser, leaving the lock for ever.


This is how I do optimistic locking in Django:

updated = Entry.objects.filter(Q(id=e.id) && Q(version=e.version))\
          .update(updated_field=new_value, version=e.version+1)
if not updated:
    raise ConcurrentModificationException()

The code listed above can be implemented as a method in Custom Manager.

I am making the following assumptions:

  • filter().update() will result in a single database query because filter is lazy
  • a database query is atomic

These assumptions are enough to ensure that no one else has updated the entry before. If multiple rows are updated this way you should use transactions.

WARNING Django Doc:

Be aware that the update() method is converted directly to an SQL statement. It is a bulk operation for direct updates. It doesn't run any save() methods on your models, or emit the pre_save or post_save signals


This question is a bit old and my answer a bit late, but after what I understand this has been fixed in Django 1.4 using:

select_for_update(nowait=True)

see the docs

Returns a queryset that will lock rows until the end of the transaction, generating a SELECT ... FOR UPDATE SQL statement on supported databases.

Usually, if another transaction has already acquired a lock on one of the selected rows, the query will block until the lock is released. If this is not the behavior you want, call select_for_update(nowait=True). This will make the call non-blocking. If a conflicting lock is already acquired by another transaction, DatabaseError will be raised when the queryset is evaluated.

Of course this will only work if the back-end support the "select for update" feature, which for example sqlite doesn't. Unfortunately: nowait=True is not supported by MySql, there you have to use: nowait=False, which will only block until the lock is released.


Actually, transactions don't help you much here ... unless you want to have transactions running over multiple HTTP requests (which you most probably don't want).

What we usually use in those cases is "Optimistic Locking". The Django ORM doesn't support that as far as I know. But there has been some discussion about adding this feature.

So you are on your own. Basically, what you should do is add a "version" field to your model and pass it to the user as a hidden field. The normal cycle for an update is :

  1. read the data and show it to the user
  2. user modify data
  3. user post the data
  4. the app saves it back in the database.

To implement optimistic locking, when you save the data, you check if the version that you got back from the user is the same as the one in the database, and then update the database and increment the version. If they are not, it means that there has been a change since the data was loaded.

You can do that with a single SQL call with something like :

UPDATE ... WHERE version = 'version_from_user';

This call will update the database only if the version is still the same.