Search code examples
mysqldjangotransactionsselect-for-update

Why doesn't "select_for_update()" work in Django with MySQL?


I have a table like below:

SELECT id, name FROM node;
+----+------+
| id | name |
+----+------+
|  5 | na   |
+----+------+

Then define below function:

>>> def foo_with_sfu(seconds):
...     with transaction.atomic():
...         node = Node.objects.select_for_update().filter(pk=5)
...         time.sleep(seconds)
...         node = Node.objects.get(pk=5)
...         print(node.name)
... 

I was hoping the select_for_update will lock row pk=5 so if I open another console to change the node.name during time.sleep, the change operation will be blocked.

But actually when I run the function, and run update sql in another console during the time.sleep, the update were not blocked.

It seems the select for update not locking up the row. Why?


Solution

  • Your select_for_update() query is never evaluated, so the lock is never executed on the database. See the documentation for when and how querysets get evaluated.

    If you simply wrap the call in a queryset-evaluating function your test should work. For example:

    with transaction.atomic():
        node = list(Node.objects.select_for_update().filter(pk=5))