Concurrency & EXPLAIN

Django ORM PostgreSQL May 22, 2026 python

select_for_update() issues SELECT ...

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# ======================================================================
# `select_for_update()` issues `SELECT ... FOR UPDATE` to lock rows for the
# transaction; `skip_locked=True` skips rows another transaction already holds
# (a simple work-queue pattern). `QuerySet.explain()` returns PostgreSQL's query
# plan — how it will execute.
# ======================================================================

# ----------------------------------------------------------------------
# select_for_update(skip_locked=True) — row locking
# ----------------------------------------------------------------------

# Django:
with transaction.atomic():
    rows = list(
        Book.objects.select_for_update(skip_locked=True).order_by("id")[:3]
    )

# SQL:
#   BEGIN
#
#   SELECT "examples_book"."id", "examples_book"."title", "examples_book"."author_id", "examples_book"."price", "examples_book"."pages", "examples_book"."published", "examples_book"."is_active", "examples_book"."tags", "examples_book"."data", "examples_book"."search", "examples_book"."title_upper"
#   FROM "examples_book"
#   ORDER BY "examples_book"."id" ASC
#   LIMIT 3 FOR UPDATE SKIP LOCKED
#
#   COMMIT

# Result:
#   locked & fetched: Harry Potter
#   locked & fetched: The Hobbit
#   locked & fetched: The Lord of the Rings

# ----------------------------------------------------------------------
# QuerySet.explain() — PostgreSQL's query plan
# ----------------------------------------------------------------------

# Django:
Book.objects.filter(price__gt=10).explain()

# Result:
#   Seq Scan on examples_book  (cost=0.00..1.05 rows=3 width=222)
#     Filter: (price > '10'::numeric)