About Locking Database Table Rows

The select_for_update() method of a queryset allows locking filtered table rows for updates within the same atomic transaction. This ensures that two concurrent Django requests don't create race conditions - that is, they don't both read and modify the same initial data, but instead process the data sequentially, one after the other.

from django.db import transaction
from myapp.models import Account

@transaction.atomic
def transfer_money(from_account_id, to_account_id, amount):
    # Lock the rows to prevent concurrent modifications
    from_account = Account.objects.select_for_update().get(
        pk=from_account_id
    )
    to_account = Account.objects.select_for_update().get(
        pk=to_account_id
    )

    # Check if from_account has sufficient balance
    if from_account.balance < amount:
        raise ValueError("Insufficient funds")

    # Perform the transfer
    from_account.balance -= amount
    to_account.balance += amount

    # Save both accounts
    from_account.save()
    to_account.save()

Tips and Tricks Programming Databases Django 6.x Django 5.2 Django 4.2 Django ORM