Update every instance in a queryset with a count a

2019-02-11 04:51发布

问题:

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?

回答1:

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.