It seems Django
's queryset.update
method execute under transaction.atomic
context manager. When would I need to do that explicitly in my code during update
? Or what will be the benefits doing it, or problems not doing it?
try:
queryset = Model.objects.filter(a=1)
if queryset.count():
with transaction.atomic():
queryset.update(a=2) # queryset will [] after this.
for item in queryset:
item.emit_event('Updated')
except:
logger.info('Exception')
My question is do I really need to have transaction.atomic():
here?
Secondly, after .update
my queryset gets empty because its a filtered queryset. how to retain the values in my case as I want to emit an event on the individual objects.
Update 30.08.2023
There is a new queryset method select_for_update
in most cases you probably want to use it instead.
First
As docs state
Atomicity is the defining property of database transactions. atomic allows us to create a block of code within which the atomicity on the database is guaranteed. If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.
In your example you would need atomic
if emit_event
is doing something and you want this update to be done only if all emit_event
function calls and queryset.update
are successfull. But if states of emit_event
does not affect your business logic of update, atomic
here would be redundant because as you said yourself update
has internal atomic
.
Second
Querysets are lazy. Which means evaluation of queryset will be done when you are iterating over it. So you need to do something like this. Answering latest comment
try:
queryset = Model.objects.filter(a=1)
item_ids = list(queryset.values_list('id', flat=True)) # store ids for later
if item_ids: # optimzing here instead of queryset.count() so there won't be hit to DB
with transaction.atomic():
queryset.update(a=2) # queryset will [] after this.
for item in Model.objects.filter(id__in=item_ids): # <- new queryset which gets only updated objects
item.emit_event('Updated')
except:
logger.info('Exception')
See there we make new queryset when iterating over it to get updated items