I am trying to update a field of a queryset atomically. I have something like this:
counter = 0
for row in myQuerySet:
row.myField = counter
counter = counter + 1
row.save()
That works, but I want to do this atomically, because I have hundreds of registers and it is a waste of time. I need something like this:
counter = 0
myQuerySet.update(myField=(counter+=1))
But that does not work. What is the correct sintax for this?
That works, but I want to do this atomically […]
Often, the answer is to use the QuerySet.update
method. That works when you want to do the same thing – or something that doesn't need to change dynamically – to all the instances in a queryset.
Since the operation you want to perform appears to need a dynamic change to each instance in turn, you can instead use the select_for_update
method.
from django.db import transaction
dolors = LoremIpsum.objects.select_for_update().filter(dolor=True)
with transaction.atomic():
counter = 0
for lorem_ipsum in dolors:
lorem_ipsum.amet = counter
counter += 1
lorem_ipsum.save()
The documentation for select_for_update
says this does what you want:
All matched entries will be locked until the end of the transaction block, meaning that other transactions will be prevented from changing or acquiring locks on them.
Because the queryset causes the items to be “locked until the end of the transaction block”, you need to perform your operations inside a transaction block using transaction.atomic
as above.