Django locking between clean() and save()

Question:

Let’s say I have a model with a unique field email:

class MyModel:

    email = models.EmailField(unique=True)

    def save(self):
        .... # save model

    def clean(self):
        .... # validate model, make sure email doesn't already exist.

Usually, if a browser submits a form with email being a value that already exists, it would raise a ValidationError, due to the model form field validation.

If two browsers submit the same email at the same time, with email being a value that doesn’t exist yet, at least one of the requests will succeed by saving a row into the database. The other request, if it arrives long enough after the first one, will be treated normally – with a ValidationError raised saying the email already exists. But if it arrives almost at the same time as the first one, then the clean() will succeed – the email doesn’t exist yet, but by the time the save() method is executed, the row from the first request would have been saved. In this latter case, an IntegrityError will be raised instead and the server would return an Internal Server 500 error, which is undesirable.

How can this last scenario be prevented? Database transactions?

Asked By: Eric

||

Answers:

Just to use Django database transactions in this case will not be enough. What you want to do when creating a new model is to use database table lock together with transaction. But Django has no API for locking tables yet, therefore you will have to use raw SQL to perform a table lock for your database regarding of its type.

If you use PostgreSQL, here is a perfect example:
http://www.caktusgroup.com/blog/2009/05/26/explicit-table-locking-with-postgresql-and-django/

If you are under some other db, you will have to investigate how to perform table lock.

Answered By: Simanas

While the use case is slightly different, you might want to read this old answer from Alex Martelli about “optimistic concurrency” approach.

Answered By: Germano
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.