Reputation: 15824
Imagine I have a python dictionary where keys are existing user ids, and values are scores to be added to those users' existing scores.
For example: {1: 1580, 4: 540, 2: 678}
(this could stretch to n
k,v pairs)
I need to update the scores of all these user objects (updated_score = original_score + new_score). One way to do it is iteratively, like so:
from django.db.models import F
scores = {1: 1580, 4: 540, 2: 678}
for user_id,score_to_add in scores.iteritems():
UserProfile.objects.filter(user_id=user_id).update(score=F('score')+score_to_add)
But that's multiple DB calls. Can I do it in a single call? An illustrative example would be great. As you would have guessed, this is for a Django project.
Upvotes: 15
Views: 13451
Reputation: 12086
Something like that:
from django.db.models import F
from django.db import transaction
with transaction.atomic():
scores = {1: 1580, 4: 540, 2: 678}
for user_id,score_to_add in scores:
UserProfile.objects.filter(user_id=user_id).update(score=F('score')+score_to_add)
You can take a look at this answer too.
[UPDATE]:
TL;DR: It'll not make one db query but it will be faster cause each query lacks the database overhead.
As the docs and @ahmed in his answer say:
Django’s default behavior is to run in autocommit mode. Each query is immediately committed to the database, unless a transaction is active.
By using
with transaction.atomic()
all the inserts are grouped into a single transaction. The time needed to commit the transaction is amortized over all the enclosed insert statements and so the time per insert statement is greatly reduced.
Upvotes: 16
Reputation: 5611
transaction.atomic()
proposed by @nik_m is good idea, but also you should get records from database in single request.
from django.db.models import F
from django.db import transaction
with transaction.atomic():
scores = {1: 1580, 4: 540, 2: 678}
users_to_update = UserProfile.objects.filter(
user_id__in=scores.keys()
)
for user in users_to_update:
user.update(score=F('score') + scores[user.user_id])
Upvotes: 0