Django Interview Q&A

Django Interview Q&A

Explore django interview q&a code snippets and tutorials

Django Interview Q&A

Dangerous schema changes on large PostgreSQL tables and how to make them safely

Every ALTER TABLE takes an ACCESS EXCLUSIVE lock.

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
# ======================================================================
# Q: Which schema changes are dangerous on a large table, and how do you perform
# them safely?
#
# Every ALTER TABLE takes an ACCESS EXCLUSIVE lock. If it waits behind a slow
# query, it blocks all the queries that follow. Rules:
# - Always SET lock_timeout = '5s' in the migration, so it fails fast instead of
#   "freezing" the site. Then retry.
# - ADD COLUMN with a constant default is fast since PostgreSQL 11 (it does not
#   rewrite the table).
# - Index: AddIndexConcurrently with atomic = False.
# - NOT NULL or FK on an existing table: first a NOT VALID constraint
#   (AddConstraintNotValid), then ValidateConstraint in a separate migration,
#   which does not block writes.
# - Column type change or rename: expand/contract (new column, dual write,
#   backfill, switch, drop).
# - Backfill in batches of 1,000-10,000 rows, with a short pause, never a single
#   UPDATE over the whole table.
# - Check the SQL before deploying with manage.py sqlmigrate. A CI tool: django-
#   migration-linter.
# ======================================================================

from django.contrib.postgres.operations import AddIndexConcurrently

class Migration(migrations.Migration):
    atomic = False
    operations = [
        migrations.RunSQL("SET lock_timeout = '5s'", migrations.RunSQL.noop),
        AddIndexConcurrently('order', models.Index(
            fields=['company', '-created_at'], name='order_company_created')),
    ]
Django Interview Q&A

Writing a simple Django middleware that times requests

Write a simple Django middleware.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ======================================================================
# Q: Write a simple Django middleware.
# ======================================================================

class RequestTimingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        start = time.perf_counter()
        response = self.get_response(request)
        response['X-Response-Time'] = f'{(time.perf_counter()-start)*1000:.1f}ms'
        return response
Django Interview Q&A

Django annotate vs aggregate, and Subquery with OuterRef

aggregate returns a dict of overall values.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# ======================================================================
# Q: What is the difference between annotate and aggregate in Django, and what
# are Subquery and OuterRef for?
#
# aggregate returns a dict of overall values. annotate adds a computed field to
# every row. Subquery/OuterRef allows correlated subqueries, e.g. "the latest
# price of each product", without N+1:
# ======================================================================

latest = Price.objects.filter(product=OuterRef('pk')).order_by('-created')
Product.objects.annotate(last_price=Subquery(latest.values('amount')[:1]))
Django Interview Q&A

Mutable default arguments in Python: what is the bug?

What is the bug with mutable default arguments in Python, and how do you fix it?

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# ======================================================================
# Q: What is the bug with mutable default arguments in Python, and how do you
# fix it?
# ======================================================================

def add(item, bucket=[]):   # WRONG: the list is created once
    bucket.append(item); return bucket

def add(item, bucket=None):  # CORRECT
    bucket = [] if bucket is None else bucket
    bucket.append(item); return bucket