updated June 29, 2019
Django ORM performance tips: changing data
The article explains how to insert, update or delete the database data with Django ORM in efficient way with less or faster SQL queries.
1. QuerySet.update() instead Model.save()
The code below executes two SQL queries: first to load the object and the second to update all the fields:user = User.objects.get(id=user_id) user.first_name = 'Alex' user.save()
QuerySet.update()
updates only the specified fields with one query:
User.objects.filter(id=user_id).update(first_name='Alex')
2. How to use field value in QuerySet.update()
UseF()
expression for the database field values instead constant or variable value:
User.objects.filter(id=user_id).update(age=F('age') + 1)that generates SQL like:
UPDATE users SET age = age + 1Less effecient
Model.save()
update that does the same:
user = User.objects.get(id=user_id) user.age += 1 user.save()There are several benefits comparing to
Model.save()
update:
- Can update several objects returned by
QuerySet.filter()
- Several same queries update correctly if run in parallel
- Don't have to load the current object state with extra SQL query
3. Insert many database objects with one SQL query
Adding object to the database is easy with Django ORM:user = User(first_name='Alex', last_name='O') user.save()but it requires one SQL per object so isn't effecient for many objects, use
Model.objects.bulk_create(model_list)
to add many values with one SQL INSERT query:
users = [User(first_name='Alex', last_name='O'), User(first_name='John', last_name='Doe')] User.objects.bulk_create(users)
4. Delete many objects with one SQL query
Good code:User.objects.filter(is_active=False).delete()Bad code that could trigger a lot of SQL queries:
for user in User.objects.filter(is_active=False): user.delete()
Many transactions per view
The code below executes two transactions to charge customer and prepare shipping:from django.db import transaction with transaction.atomic(): charge_account(order.customer) order.paid = True order.save() with transaction.atomic(): make_shipping(order)Benefits comparing to single transaction:
- Easier handling of failed transaction: if the shipping transaction would fail, the charge transaction isn't cancelled and can process paid order with failed shipping later, if such scenario is possible
- Less database locking: before the transaction 2 starts, the transaction 1 database row and table locks are released so the deadlock chance is reduced