Django interview questions for experienced candidates go well past "what is MVT?" Hiring managers want proof you have operated production systems—fixed N+1 queries hiding inside DRF serializers, rolled out migrations without locking a million-row table, and understood why transaction.on_commit matters before enqueueing Celery work. Loops for experienced professionals and Python Django backend roles in 2026 blend ORM depth, Django REST Framework design, security defaults, and operational judgment (caching, workers, ASGI limits).
Below are 40+ Django interview questions with detailed explanations and strong answers you can rehearse aloud. Pair this guide with Python developer interview questions for language fundamentals, PostgreSQL interview questions for a common production Django database, full stack developer interviews for React-to-Django integration, SQL technical interviews when query plans matter, and Flask SQLAlchemy if interviewers compare lightweight Flask patterns.
Interview context and how to prepare
What Django interviews test
Django interviews for experienced roles test whether you can build and operate web applications—not recite admin registration steps from memory.
| Layer | What interviewers probe |
|---|---|
| Request pipeline | URLs, middleware, views, templates/serializers |
| ORM | Relationships, query counts, transactions, constraints |
| Migrations | Safe rollout, squashing, backward compatibility |
| Security | CSRF, XSS, auth, object-level permissions |
| APIs (DRF) | Serializers, validation, throttling, versioning |
| Production | Caching, Celery, settings split, check --deploy |
| Architecture | Service layer, app boundaries, when not to use signals |
| Experience | What changes |
|---|---|
| Mid (3–5 yrs) | CRUD, ORM basics, DRF serializers |
| Senior (5–8 yrs) | N+1, transactions, permissions, testing strategy |
| Staff / 8+ yrs | Large-project structure, migration ops, multi-tenant patterns |
Typical Django interview loop
| Round | Duration | Focus |
|---|---|---|
| Recruiter / HM | 30 min | Projects, stack (DRF, Celery, Postgres), team size |
| Django deep dive | 60–90 min | ORM, middleware, security, DRF |
| Live exercise | 45–90 min | Write view/serializer, fix N+1, design models |
| System design | 45–60 min | Monolith modules, workers, caching—senior roles |
| Behavioral | 30–45 min | Incidents, code review, mentoring |
Take-home tasks may ask for a small DRF API with tests, pagination, and a README explaining trade-offs.
Realistic Django prep plan
| Week | Focus | Output |
|---|---|---|
| 1 | Request pipeline — URLs, middleware, settings | Diagram request → response; list your project's middleware |
| 2 | ORM — relationships, select_related, prefetch_related |
Fix one N+1; use assertNumQueries in a test |
| 3 | DRF — serializers, permissions, pagination | Build CRUD API with object-level checks |
| 4 | Migrations & transactions — atomic, on_commit, select_for_update |
Document one safe migration rollout |
| 5 | Security & deployment — check --deploy, secrets, caching |
Harden settings checklist for a sample project |
| 6 | Scenarios + STAR stories | Rehearse slow API debug and failed Celery recovery |
Keep one reference Django project (blog, orders API, or internal tool) you can whiteboard.
Django developer vs Python developer — what is the extra bar?
What interviewers are testing: Whether you understand the extra framework depth expected beyond Python language skill—ORM, request pipeline, DRF, migrations, and production operations.
A Python developer loop may stay language-heavy: decorators, GIL, asyncio, general algorithms.
A Django developer loop assumes solid Python and adds framework production depth:
| Topic | Python-only loop | Django experienced loop |
|---|---|---|
| Web | Maybe Flask/FastAPI sketch | Full MVT/DRF lifecycle |
| Data | SQL strings or generic ORM | Migrations, managers, F()/Q() |
| Concurrency | asyncio basics | ASGI limits, Celery, on_commit |
| Security | Generic OWASP awareness | CSRF middleware, ALLOWED_HOSTS, DRF auth |
| Testing | pytest | pytest-django, assertNumQueries |
See Python developer interviews for language prep; return here for Django-specific depth.
A strong answer is:
A Django developer needs strong Python plus framework depth: request lifecycle, ORM behavior, migrations, middleware, authentication, DRF, caching, and deployment. For experienced roles, I also expect to explain production issues such as N+1 queries, transaction boundaries, and safe migrations.
MVT architecture and the request lifecycle
Explain MVT architecture. How is it different from MVC?
What interviewers are testing: Whether you understand Django's terminology well enough to trace responsibility between models, views, templates, and URL routing rather than mechanically mapping MVT labels to MVC.
Django follows MVT: Model–View–Template.
| Layer | Role |
|---|---|
| Model | Data layer: database tables, fields, relationships, ORM queries |
| View | Request handler: receives request, applies business logic, returns response |
| Template | Presentation layer: renders HTML shown to the user |
The confusing part is the word View.
In many MVC frameworks, the Controller handles the request. In Django, that controller-like responsibility is handled by the view.
| MVC term | Rough Django equivalent |
|---|---|
| Model | Model |
| View | Template |
| Controller | View |
Typical flow:
URL pattern
→ Django view
→ model/service/ORM
→ template or JSON responseExample:
# urls.py
urlpatterns = [
path("orders/<int:pk>/", views.order_detail, name="order-detail"),
]# views.py
def order_detail(request, pk):
order = Order.objects.get(pk=pk)
return render(request, "orders/detail.html", {"order": order})For API-only Django apps, templates may not be used much. A Django REST Framework app may use serializers and Response objects instead of HTML templates, but the high-level pipeline is still request → URL → view → response.
Important interview nuance:
- Models should not become a dumping ground for all business logic
- Views should not become huge “God functions”
- Complex business workflows often belong in service/domain layers
- Templates should avoid heavy database work
A strong answer is:
Django's MVT maps closely to MVC, but Django's view acts like the controller. Models represent data, views handle request logic, and templates render presentation. For APIs, serializers often replace templates as the output-shaping layer.
Walk through the Django request lifecycle step by step.
What interviewers are testing: Whether you can trace a request from the server interface through middleware and URL resolution to the view and back, and use that model to diagnose authentication, CSRF, routing, and middleware-order problems.
A weak answer says “URL goes to a view.” A strong answer traces the full request path.
Typical Django request lifecycle:
- Web server receives the HTTP request
- WSGI or ASGI server passes it to Django
- Request enters middleware in the order listed in
settings.MIDDLEWARE - URL resolver matches the path using
ROOT_URLCONF - Django calls the matched view
- View runs application logic, ORM queries, services, permissions, or forms
- View returns an
HttpResponse,JsonResponse, redirect, or DRFResponse - Response travels back through middleware in reverse order
- Server sends the final HTTP response to the client
Flow:
Client
→ web server
→ WSGI/ASGI server
→ middleware request phase
→ URL resolver
→ view
→ response object
→ middleware response phase
→ clientExample URL route:
urlpatterns = [
path("orders/<int:pk>/", OrderDetailView.as_view(), name="order-detail"),
]Important middleware order example:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
]Why order matters:
| Area | Why order matters |
|---|---|
| Sessions | Auth middleware depends on session middleware |
| Authentication | request.user is attached before views need it |
| CSRF | Unsafe methods can be rejected before view logic |
| Security headers | Responses can be modified globally |
| Custom tenant middleware | Tenant may need to be resolved before view/service logic |
Debugging examples:
| Symptom | Check |
|---|---|
request.user is missing/wrong |
AuthenticationMiddleware, session middleware order |
| CSRF failure before view | CsrfViewMiddleware, token, trusted origins |
| View not called | URL pattern, middleware short-circuit, permissions |
| Wrong tenant/database | Tenant middleware order |
| Response header missing | Middleware ordering or early return |
A strong answer is:
A request enters Django through WSGI or ASGI, passes through middleware, gets routed by URLconf to a view, and the response unwinds through middleware in reverse order. Middleware order matters for sessions, auth, CSRF, security, and custom cross-cutting logic.
What is middleware and what are common production uses?
What interviewers are testing: Whether you define middleware and what are common production uses with mechanism and implications—not a one-line buzzword.
Middleware is Django’s global request/response hook system.
Each middleware wraps the view layer. It can:
- Inspect or modify the request before the view runs
- Return a response early without calling the view
- Inspect or modify the response after the view runs
- Handle cross-cutting concerns consistently
Common built-in middleware:
| Middleware | Purpose |
|---|---|
SecurityMiddleware |
Security-related headers and SSL redirects |
SessionMiddleware |
Adds session support |
AuthenticationMiddleware |
Adds request.user |
CsrfViewMiddleware |
Protects unsafe requests from CSRF |
CommonMiddleware |
Common URL handling behavior |
GZipMiddleware |
Compresses responses when appropriate |
Custom production middleware examples:
| Use case | Example |
|---|---|
| Request tracing | Add X-Request-ID or correlation ID |
| Metrics | Measure request duration/status codes |
| Multi-tenancy | Resolve tenant from host/header/path |
| Feature flags | Attach rollout flags to request |
| Security | Add custom headers or block suspicious requests |
| Auditing | Log user/IP/action metadata |
| Maintenance mode | Short-circuit with 503 response |
Simple middleware shape:
class RequestIDMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.request_id = generate_request_id()
response = self.get_response(request)
response["X-Request-ID"] = request.request_id
return responseMiddleware behaves like an onion:
middleware A request phase
middleware B request phase
view
middleware B response phase
middleware A response phaseCommon mistakes:
- Putting middleware in the wrong order
- Doing heavy database queries for every request
- Swallowing exceptions without logging
- Adding tenant/auth logic too late
- Using middleware for feature-specific business logic
- Forgetting async compatibility in ASGI-heavy apps
Middleware should be used for cross-cutting concerns, not normal view-specific logic.
A strong answer is:
“Middleware wraps Django’s request/response cycle. I use it for cross-cutting concerns like security, sessions, auth, tracing, metrics, tenant resolution, and request IDs, while keeping feature-specific business logic in views or services.”
WSGI vs ASGI — when does Django use each?
What interviewers are testing: Whether you understand WSGI and ASGI as deployment interfaces and can choose sync versus async-capable stacks based on concurrency needs—not treat them as interchangeable labels.
WSGI and ASGI are server interfaces used to run Django applications.
| Interface | Model | Typical server | Best for |
|---|---|---|---|
| WSGI | Synchronous request/response | Gunicorn, uWSGI | Traditional Django apps |
| ASGI | Async-capable protocol interface | Uvicorn, Daphne, Hypercorn | Async views, websockets, long-lived connections |
WSGI is enough for many Django apps because most traditional Django work is database-heavy and synchronous.
ASGI is useful when the app needs:
- Async views
- WebSockets
- Long-polling
- Server-sent events
- Many concurrent external API calls
- Django Channels
- Mixed HTTP and async protocols
Example async view:
async def user_detail(request, pk):
user = await User.objects.aget(pk=pk)
return JsonResponse({"name": user.name})Important ORM warning:
Django supports async views, but you must be careful with synchronous ORM/database work inside async code. Sync views can still run under ASGI; async views can run under WSGI with adaptation, though without the benefits of a fully asynchronous request stack.
Risky pattern:
async def user_detail(request, pk):
user = User.objects.get(pk=pk) # synchronous ORM in async context
return JsonResponse({"name": user.name})Safer options:
- Keep ORM-heavy views synchronous when that is simpler
- Use native async ORM methods such as
aget(),acreate(), andasync forwhere supported - Use
sync_to_async()to bridge code that still lacks an async API - Avoid wrapping huge DB-heavy code in async just for trendiness
Example bridge:
from asgiref.sync import sync_to_async
async def user_detail(request, pk):
user = await sync_to_async(User.objects.get)(pk=pk)
return JsonResponse({"name": user.name})Interview nuance:
| Scenario | Better choice |
|---|---|
| Normal synchronous CRUD app | WSGI is sufficient; ASGI is also valid if the deployment needs async capabilities elsewhere |
| API calls multiple external services concurrently | ASGI + async views can help |
| WebSockets/chat/live notifications | ASGI + Channels |
| Heavy CPU work | Background worker, not async view |
| Long-running job | Celery/RQ/background task |
Do not say “ASGI is always faster.” It helps when you actually have non-blocking I/O. If your view mostly blocks on the synchronous ORM, async may add complexity without improving throughput.
A strong answer is:
I use WSGI for a conventional synchronous Django stack and ASGI when I need high-concurrency async I/O, streaming, long-lived connections, or WebSockets. Sync views can still run under ASGI; for async views I use Django's async ORM APIs where available and bridge remaining synchronous code deliberately.
Models, ORM, and migrations
How does the Django ORM and QuerySet lazy evaluation work?
What interviewers are testing: Whether you have depth on how does the django orm and queryset lazy evalu...—mechanism, trade-offs, and production judgment.
Django models map Python classes to database tables, and the ORM lets you build SQL queries using Python objects.
A QuerySet is lazy. It represents a database query, but it usually does not hit the database until it is evaluated.
Example:
qs = Order.objects.filter(status="PAID").order_by("-created_at")At this point, Django has built a query, but it has not necessarily executed SQL yet.
Common QuerySet evaluation triggers:
| Action | Effect |
|---|---|
| Iteration | Executes query |
list(qs) |
Executes query and loads all rows |
len(qs) |
Evaluates results; use count() for count query |
bool(qs) |
Evaluates existence; use exists() for existence check |
qs[0] |
Executes query with limit/offset |
repr(qs) in shell |
May evaluate for display |
| Template loop | Executes query while rendering |
| Serialization | Often evaluates the QuerySet |
Example:
# Lazy
orders = Order.objects.filter(status="PAID")
# Evaluates
for order in orders:
print(order.id)QuerySets are chainable:
orders = (
Order.objects
.filter(status="PAID")
.filter(total__gte=100)
.select_related("customer")
)Important caching behavior:
- The same evaluated QuerySet caches its results
- A new QuerySet clone runs a new query
- Calling
.all()creates a new QuerySet - Related manager calls can trigger extra queries
- Lazy evaluation can hide N+1 query bugs
Better count/existence patterns:
Order.objects.filter(status="PAID").count()
Order.objects.filter(status="PAID").exists()Instead of:
len(Order.objects.filter(status="PAID"))
bool(Order.objects.filter(status="PAID"))Interviewers like candidates who can connect QuerySet laziness to performance debugging.
Useful tools:
- Django Debug Toolbar
connection.queriesin developmentassertNumQueries()in tests- Database logs
QuerySet.explain()for query plans
A strong answer is:
“A QuerySet is a lazy, chainable SQL query description. It hits the database only when evaluated, so I pay attention to loops, serializers, templates,
len(),bool(), and related-object access that can trigger hidden queries.”
What is the difference between null=True and blank=True?
What interviewers are testing: Whether you distinguish en null=true and blank=true with correct terms and a real usage example.
null=True and blank=True work at different layers.
| Option | Layer | Meaning |
|---|---|---|
null=True |
Database | Column can store SQL NULL |
blank=True |
Validation/forms | Field is allowed to be empty in forms/model validation |
Example:
class Profile(models.Model):
bio = models.TextField(blank=True)
birth_date = models.DateField(null=True, blank=True)Common patterns:
| Field | Common choice | Why |
|---|---|---|
| Required string | default null=False, blank=False |
Must be provided |
| Optional string | blank=True, default="" |
Avoid two empty values |
| Optional date | null=True, blank=True |
No date is naturally NULL |
| Optional FK | null=True, blank=True |
Relationship may be missing |
| Boolean | Avoid nullable unless tri-state needed | True/False/Unknown only when required |
Why avoid null=True on strings?
Because you create two ways to represent “empty”:
NULL
""That can complicate filters, uniqueness, validation, and API behavior.
Example problem:
Customer.objects.filter(nickname="")
Customer.objects.filter(nickname__isnull=True)Now both may mean “no nickname.”
Use blank=True when form/admin/serializer validation should accept an empty value.
Use null=True when the database needs to represent missing value as SQL NULL.
Important nuance: Django REST Framework serializers have their own options such as allow_blank and allow_null, but the same idea applies: blank string and null are different states.
A strong answer is:
“
null=Truecontrols database NULL storage, whileblank=Truecontrols validation. I usually avoidnull=Trueon string fields so I do not end up with both NULL and empty string representing the same thing.”
How do F() and Q() objects help in Django queries?
What interviewers are testing: Whether you can explain how do f() and q() objects help in django queries with correct definitions and one concrete example.
F() and Q() help push more logic into SQL instead of doing it in Python loops.
F() references a model field in the database.
Use it for:
- Atomic increments/decrements
- Comparing two columns
- Updating one field based on another
- Avoiding race-prone read-modify-write code
Example stock update:
from django.db.models import F
Product.objects.filter(pk=product_id, stock__gt=0).update(
stock=F("stock") - 1
)This is safer than:
product = Product.objects.get(pk=product_id)
product.stock -= 1
product.save()The second version can lose updates under concurrency.
Q() builds complex boolean conditions.
Use it for:
- OR conditions
- NOT conditions
- Dynamic filters
- Combining optional search filters
Example:
from django.db.models import Q
orders = Order.objects.filter(
Q(status="PAID") | Q(status="SHIPPED"),
total__gte=100,
)Negation:
Order.objects.filter(~Q(status="CANCELLED"))Dynamic filter example:
query = Q()
if status:
query &= Q(status=status)
if search:
query &= Q(customer__name__icontains=search) | Q(reference__icontains=search)
orders = Order.objects.filter(query)Comparison:
| Tool | Use |
|---|---|
F() |
Field references and atomic DB-side updates |
Q() |
Complex WHERE conditions |
annotate() |
Add computed values to rows |
Case/When |
Conditional SQL expressions |
select_for_update() |
Lock rows during a transaction |
Important concurrency note:
F() helps avoid lost updates for simple field updates, but complex business transactions may still need transaction.atomic() and select_for_update().
A strong answer is:
“
F()lets the database update or compare fields atomically without pulling values into Python.Q()lets me build OR, NOT, and dynamic filters cleanly. Both help keep filtering and updates in SQL.”
Explain transaction.atomic, on_commit, and select_for_update.
What interviewers are testing: Your mental model of transaction.atomic, on_commit, and select_for_update.—how it works and what breaks if you get it wrong.
Transactions make multi-step database operations safe.
transaction.atomic() creates an all-or-nothing block:
from django.db import transaction
with transaction.atomic():
order = Order.objects.create(customer=customer)
Payment.objects.create(order=order, amount=order.total)If an exception occurs inside the block, Django rolls back the transaction.
Use transactions for:
- Money transfer
- Inventory decrement + order creation
- Multi-table writes
- State transitions
- Operations that must not be partially saved
select_for_update() locks selected rows until the transaction finishes.
Example:
@transaction.atomic
def reserve_stock(product_id, quantity):
product = (
Product.objects
.select_for_update()
.get(pk=product_id)
)
if product.stock < quantity:
raise OutOfStock()
product.stock -= quantity
product.save(update_fields=["stock"])This prevents two requests from reading the same stock value and overselling.
transaction.on_commit() runs a callback only after the outer transaction successfully commits.
Use it for side effects:
- Send email
- Enqueue Celery task
- Invalidate cache
- Publish event
- Enqueue post-commit work that calls an external service
Example:
def create_order(request):
with transaction.atomic():
order = Order.objects.create(...)
transaction.on_commit(
lambda: send_receipt.delay(order.id)
)Why this matters:
If you enqueue a Celery task before commit, the worker may run before the row is committed and fail to find the object.
Bad pattern:
with transaction.atomic():
order = Order.objects.create(...)
send_receipt.delay(order.id) # worker may run too earlyGood pattern:
with transaction.atomic():
order = Order.objects.create(...)
transaction.on_commit(lambda: send_receipt.delay(order.id))Important interview nuance:
- Keep
on_commit()callbacks lightweight; enqueue slow or retryable I/O to a background worker - Keep transactions short
- Avoid slow network calls inside transactions
- Lock rows in a consistent order to reduce deadlocks
- Use database constraints too, not only application checks
select_for_update()behavior depends on database support and isolation level- Catch database exceptions outside the atomic block when possible
A strong answer is:
“I use
atomic()for all-or-nothing writes,select_for_update()to lock rows when concurrent updates can race, andon_commit()for side effects like Celery tasks so workers only see committed data.”
How do Django migrations work and how do you deploy them safely?
What interviewers are testing: Whether you can design backward-compatible schema changes, understand locking and rollout risk on large tables, and verify migrations before production deploys.
Django migrations are versioned database schema changes.
They are generated from model changes and stored as Python files in each app’s migrations/ directory.
Common commands:
| Command | Use |
|---|---|
makemigrations |
Create migration files from model changes |
migrate |
Apply unapplied migrations to the database |
showmigrations |
Show migration status |
sqlmigrate |
Show SQL for a migration |
migrate --plan |
Preview migration plan |
makemigrations --check --dry-run |
CI check for missing migrations |
Example:
python manage.py makemigrations
python manage.py migrate
python manage.py showmigrations
python manage.py migrate --planMigrations are like version control for schema. They should be reviewed, tested, and deployed carefully.
Safe production migration principles:
| Practice | Why |
|---|---|
| Backward-compatible changes | Old and new code may run during deploy |
| Expand-contract pattern | Avoid breaking rolling deployments |
| Avoid long locks | Large tables can block writes/reads |
| Backfill in batches | Prevent long transactions and load spikes |
| Add indexes carefully | Large indexes can lock or slow production |
| Separate schema and data migration | Easier rollback and timing control |
| Test on production-sized data | Small staging DB may hide lock problems |
| Keep migrations deterministic | Avoid environment-specific surprises |
Expand-contract example:
1. Add nullable column
2. Deploy code that writes both old and new fields
3. Backfill old rows in batches
4. Deploy code that reads new field
5. Add NOT NULL/unique constraint later
6. Remove old column after safe windowRisky migration examples:
- Add or validate constraints on very large tables without checking locking behavior
- Perform schema operations that may rewrite a large table during peak traffic
- Run a large data backfill in one migration/transaction
- Drop or rename fields while old application instances still depend on them
- Add index during peak traffic without planning
- Run migration without backup/rollback plan
Inspect the actual SQL with sqlmigrate and understand the production database's locking behavior rather than assuming every generated migration is equally safe.
Useful CI checks:
python manage.py makemigrations --check --dry-run
python manage.py migrate --plan
python manage.py testOperational advice:
- Run migrations before or during deploy based on compatibility
- Make migrations safe for rolling deploys
- Coordinate app and worker releases
- Check migration duration in staging
- Monitor DB locks, errors, and latency
- Keep backups before risky schema changes
Common interview mistake:
“Just run migrate before deployment.”
For small apps, that may be fine. For large production tables, safe migration design matters more than the command.
A strong answer is:
“Django migrations are versioned schema changes. I deploy them safely with backward-compatible steps, expand-contract changes, batched backfills, lock-aware operations, and CI checks like
makemigrations --check --dry-runandmigrate --plan.”
Views, URLs, and templates
Function-based views vs class-based views — when do you use each?
What interviewers are testing: Whether you contrast function-based views and class-based views — when do you use each with when each wins—not parallel definitions.
| Style | Pros | Cons |
|---|---|---|
| FBV | Explicit, easy to read, great for small endpoints | Repetition without decorators |
| CBV | Reuse via mixins (ListView, CreateView) |
Harder to trace for juniors |
| DRF ViewSets | Router registration, consistent CRUD | Magic if team does not know DRF |
Experienced guidance:
- FBV for one-off admin tools or complex branching
- CBV/ViewSets for consistent CRUD with permissions and pagination
- Keep views thin—move rules to services or selectors
class OrderListView(ListView):
model = Order
paginate_by = 25A strong answer is:
I pick FBVs for clarity on small endpoints and DRF ViewSets for API resources, but business logic lives in services—not in view methods regardless of style.
How does URL routing work with path, include, and namespaces?
What interviewers are testing: Whether you understand how URL patterns, includes, and namespaces keep routing modular and make reverse() stable as apps grow.
ROOT_URLCONF points to your root urls.py. Patterns map paths to views:
from django.urls import path, include
urlpatterns = [
path("api/v1/orders/", include("orders.urls", namespace="orders")),
path("health/", health_check),
]| Feature | Use |
|---|---|
path() / re_path() |
Match URL to view |
include() |
Delegate to app urls |
namespace + app_name |
Reverse URLs without collision — reverse("orders:detail", kwargs={"pk": 1}) |
| Named groups | Capture typed parameters |
API versioning: URL prefix (/api/v1/), Accept header, or subdomain—pick one and document for clients.
A strong answer is:
URLs are explicit maps from paths to callables; I use
includeand namespaces so apps stay modular andreverse()stays stable when paths change.
How do Django templates work and how do you avoid XSS?
What interviewers are testing: Whether you can explain how do django templates work and how do you avoid xss with correct definitions and one concrete example.
Django templates use auto-escaping by default—HTML from variables is escaped unless marked safe.
| Construct | Role |
|---|---|
{{ variable }} |
Output escaped HTML |
{% for %}, {% if %} |
Control flow |
{% url %} |
Reverse named routes |
{% csrf_token %} |
CSRF token in forms |
{% extends %} / {% block %} |
Layout inheritance |
XSS risk returns when you use |safe or mark_safe() on user content—interviewers want you to justify every safe mark.
For SPAs, templates matter less; in API applications, DRF serializers often become the representation and validation layer, shaping data sent to and accepted from clients.
A strong answer is:
Templates auto-escape output; I never mark user-controlled strings safe, and I keep presentation in templates or serializers—not mixed into ORM models.
What is the service layer pattern in large Django projects?
What interviewers are testing: Whether you define service layer pattern in large django projects with mechanism and implications—not a one-line buzzword.
As projects grow, fat views and fat models become hard to test. A service layer (or selectors for reads) centralizes business rules:
views.py → HTTP, permissions, input/output
services.py → business transactions, orchestration
selectors.py → complex read queries (optional)
models.py → fields, constraints, small methodsBenefits:
- One place for
transaction.atomic()and domain rules - Reuse from management commands, Celery tasks, and admin actions
- Unit test without HTTP request factory
Anti-pattern: hiding business logic in signals—hard to trace; prefer explicit service calls.
A strong answer is:
Views stay thin; services own transactions and domain rules so the same logic runs from APIs, tasks, and management commands with one test suite.
Middleware, security, and deployment settings
How does Django protect against CSRF, XSS, and SQL injection?
What interviewers are testing: Whether you understand Django's built-in protections for common web threats and which production settings and deployment checks still require explicit configuration.
| Threat | Django mechanism |
|---|---|
| SQL injection | ORM parameterizes queries; raw SQL needs careful params |
| XSS | Template auto-escaping; cautious ` |
| CSRF | CsrfViewMiddleware + token on unsafe methods |
| Clickjacking | X-Frame-Options middleware |
| Host header attacks | ALLOWED_HOSTS |
| Session hijacking | SESSION_COOKIE_SECURE, HttpOnly, rotation |
Deployment checklist:
python manage.py check --deployMust-know production settings:
DEBUG=False- Strong
SECRET_KEYfrom environment SECURE_SSL_REDIRECT, HSTS when behind HTTPS- Least-privilege database user
A strong answer is:
Django ships secure defaults, but I still run
check --deploy, keep DEBUG off, configure ALLOWED_HOSTS, and never bypass ORM parameterization with string-concatenated raw SQL.
How does CSRF work with Django and a React SPA?
What interviewers are testing: Whether you can explain how does csrf work with django and a react spa with correct definitions and one concrete example.
Session-cookie auth from a SPA requires CSRF awareness:
| Approach | CSRF handling |
|---|---|
| Cookie session + Django templates | {% csrf_token %} in forms |
| SPA with session cookie | Read csrftoken cookie; send X-CSRFToken header on mutations |
| JWT in Authorization header | CSRF not applicable to header; CORS matters instead |
Also configure CORS (django-cors-headers) for allowed origins—see full stack interviews.
For a frontend hosted on a different trusted origin that makes unsafe cookie-authenticated requests, configure CSRF_TRUSTED_ORIGINS appropriately in addition to CORS.
A strong answer is:
If the browser sends session cookies, I use CSRF tokens on unsafe methods; for token-in-header APIs I focus on CORS and never store JWTs in cookies without a CSRF strategy.
How do you structure settings for dev, staging, and production?
What interviewers are testing: Whether you can split configuration by environment, load secrets safely, and keep production security settings strict without duplicating fragile configuration logic.
Split settings modules instead of one giant settings.py:
config/
settings/
base.py # INSTALLED_APPS, middleware, shared config
dev.py # DEBUG=True, local DB
staging.py
production.py # security flags, logging, caches# manage.py or wsgi.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")| Practice | Detail |
|---|---|
| 12-factor | Secrets from environment / secret manager |
| Never commit | SECRET_KEY, DB passwords |
| Feature flags | Environment-driven toggles |
| Database | Separate DB per environment |
A strong answer is:
I inherit from a base settings module and override per environment, loading secrets from the platform—not from git—and I keep DEBUG and ALLOWED_HOSTS strict in production.
What is the difference between authentication and object-level authorization?
What interviewers are testing: Whether you distinguish en authentication and object-level authorization with correct terms and a real usage example.
Authentication answers who is the user (request.user).
Authorization answers what they may do—at URL level or per object.
| Layer | Example |
|---|---|
| Login | Session, token, or SSO |
| View permission | IsAuthenticated, IsAdminUser (DRF) |
| Object permission | "May this user edit this order?" |
Implement object checks in:
- DRF
has_object_permissionon custom permission classes - Service layer guards before mutations
- QuerySet filtering —
Order.objects.filter(owner=request.user)so unauthorized rows do not appear
Never rely on hiding UI alone—enforce on the server.
A strong answer is:
Authentication identifies the user; authorization filters QuerySets and checks object permissions before any update—defense in depth, not security by obscurity.
When would you write custom middleware vs a DRF permission or decorator?
What interviewers are testing: Whether you contrast when would you write custom middleware and a drf permission or decorator with when each wins—not parallel definitions.
| Tool | Best for |
|---|---|
| Middleware | Every request—logging, request ID, tenant from subdomain |
| Decorator | Single view concerns |
| DRF permission | API authz tied to view/action |
| Service layer | Business rules |
Avoid heavy DB work in middleware because it runs on every request that reaches Django, including lightweight health/API requests unless explicitly bypassed.
A strong answer is:
Middleware is for global cross-cutting hooks; permissions and services handle authz and business rules closer to the domain.
Django REST Framework
How do DRF serializers work and what pitfalls hit experienced teams?
What interviewers are testing: Whether you understand that serializers define an API contract and validation boundary, while query efficiency and business transactions still need to be handled deliberately outside nested serialization logic.
Serializers convert between Django models/querysets and JSON (validation, nested relations, write paths).
| Type | Use |
|---|---|
Serializer |
Explicit fields, non-model input |
ModelSerializer |
CRUD from Meta.model |
ListSerializer |
Bulk operations |
Pitfalls:
| Pitfall | Fix |
|---|---|
| N+1 in nested serializers | select_related / prefetch_related in view get_queryset |
| Over-exposing fields | Explicit fields list; never __all__ in public APIs |
Fat create()/update() |
Delegate to services; keep transactions clear |
| Validation only in serializer | Duplicate critical checks in services for non-HTTP entry points |
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["id", "status", "total", "customer_id"]
read_only_fields = ["id"]A strong answer is:
Serializers define API contracts and validation; I optimize queryset fetching for nested data and keep write paths thin by calling services inside
create/update.
Explain ViewSets, routers, and when not to use them.
What interviewers are testing: Your mental model of viewsets, routers, and when not to use them.—how it works and what breaks if you get it wrong.
ViewSets bundle CRUD actions (list, retrieve, create, …). Routers register URL patterns automatically:
router = DefaultRouter()
router.register(r"orders", OrderViewSet, basename="order")| Good fit | Poor fit |
|---|---|
| Standard resource CRUD and resource-specific actions | Workflows that don't map naturally to a resource or share little behavior with the ViewSet |
| Consistent permissions per resource | Highly heterogeneous handlers |
POST /orders/{id}/cancel/ can be modeled cleanly with @action(detail=True, methods=["post"]).
A strong answer is:
ViewSets plus routers speed up consistent CRUD APIs; for odd endpoints I use APIView or
@actionrather than forcing everything into list/retrieve semantics.
How do pagination, filtering, and throttling work in DRF?
What interviewers are testing: Whether you can protect list endpoints with pagination and allowlisted filters while using throttling as application-level usage control—not as a substitute for edge protection.
| Feature | Purpose |
|---|---|
| Pagination | PageNumberPagination, CursorPagination for large tables |
| Filtering | django-filter backend — ?status=PAID |
| Ordering | OrderingFilter with allowlist |
| Throttling | Rate limits per user/IP — AnonRateThrottle, UserRateThrottle |
Cursor pagination avoids expensive OFFSET on huge tables—aligns with SQL pagination guidance.
Always allowlist filter and order fields—prevent sorting by arbitrary columns (DoS vector).
DRF throttling is application-level usage control, not a replacement for edge/WAF/rate limiting against volumetric abuse.
A strong answer is:
I paginate every list endpoint, allowlist filters and ordering, and throttle public APIs to protect the database from scrape or abuse.
What authentication classes are common in DRF production APIs?
What interviewers are testing: Whether you can choose session, token, or JWT authentication based on client type and operational needs, and always pair authentication with authorization.
| Class | Pattern |
|---|---|
SessionAuthentication |
Same-site browser apps with CSRF |
TokenAuthentication |
Simple tokens (consider rotation) |
JWTAuthentication |
Stateless APIs (djangorestframework-simplejwt) |
| OAuth2 / social | Third-party identity |
Production notes:
- Prefer short-lived access + refresh tokens for JWT
- Store refresh tokens securely; support revocation
- Combine with permission classes and object checks
A strong answer is:
I pick session auth for same-site apps and JWT for SPA/mobile APIs, always pairing authentication with permissions and object-level checks—not open endpoints after login.
How do you version a Django REST API?
What interviewers are testing: Whether you can version APIs without breaking existing clients and communicate deprecation with a clear migration path.
Strategies:
| Strategy | Example |
|---|---|
| URL path | /api/v1/orders/ |
| Accept header | Accept: application/vnd.myapp.v1+json |
| Query param | ?version=1 (less common) |
Experienced practice:
- Deprecate with sunset headers and changelog
- Maintain serializers per version when shapes diverge
- Do not break v1 clients when adding v2—run both during migration
A strong answer is:
I version in the URL or Accept header, document deprecation timelines, and keep old serializers until clients migrate—never silently change field meaning.
Authentication, admin, and permissions
When and how do you extend the Django User model?
What interviewers are testing: Whether you choose AbstractUser, AbstractBaseUser, or a separate profile model based on whether the authentication identity itself needs customization—and set AUTH_USER_MODEL before the first migration when it does.
Django recommends customizing early:
| Approach | When |
|---|---|
| AbstractUser | New projects that want a swappable user model while retaining Django's standard auth behavior |
| AbstractBaseUser + PermissionsMixin | Authentication identity needs substantial customization |
| OneToOne profile model | Additional domain/profile data that does not need to live on the authentication model |
class User(AbstractUser):
department = models.CharField(max_length=64, blank=True)Set AUTH_USER_MODEL before first migrate—changing later is painful.
A strong answer is:
For new projects I decide early whether the authentication model itself needs customization and set
AUTH_USER_MODELbefore initial migrations when it does. I keep unrelated profile/domain data in separate models when that produces cleaner boundaries.
Session authentication vs token/JWT — trade-offs in Django?
What interviewers are testing: Whether you contrast session authentication and token/jwt — trade-offs in django with when each wins—not parallel definitions.
| Factor | Session (cookie) | Token / JWT |
|---|---|---|
| State | Server-side session store | Often stateless JWT |
| Logout | Delete session server-side | Need blocklist or short TTL |
| CSRF | Required for cookie auth | Header tokens avoid CSRF |
| Mobile / SPA | Awkward cross-domain | Common fit |
| Scale | Shared session backend (Redis) at scale | Fewer server lookups |
Hybrid: session for admin, JWT for public API—justify operational cost.
A strong answer is:
Sessions give server-controlled logout for browser apps; JWT suits distributed APIs if I handle expiry, rotation, and revocation explicitly.
How do you customize Django Admin for production teams?
What interviewers are testing: Whether you treat admin as an internal operations tool with appropriate permissions, auditing, and access controls—not as a public customer API.
Admin is powerful for internal ops—not a public API.
| Customization | Use |
|---|---|
list_display, search_fields, list_filter |
Operator efficiency |
readonly_fields |
Audit-sensitive models |
inlines |
Edit related rows together |
Custom ModelAdmin actions |
Bulk operations (with permissions) |
has_change_permission |
Object-level admin authz |
Protect admin with HTTPS, strong staff authentication/MFA, least privilege, logging, and rate limiting; where the environment allows it, additionally restrict access through VPN, identity-aware proxy, or IP/network controls.
A strong answer is:
Admin is for trusted operators. I enforce strong authentication and staff permissions, audit sensitive actions, and add network restrictions where practical rather than treating the admin as a customer-facing application.
How do Django groups and permissions work?
What interviewers are testing: Whether you can explain how do django groups and permissions work with correct definitions and one concrete example.
Django's auth model provides permissions (app_label.action_modelname) and groups that bundle permissions.
| Layer | Scope |
|---|---|
user.has_perm('orders.change_order') |
Model-level |
Custom permissions in Meta |
Business capabilities |
DRF DjangoModelPermissions |
Maps HTTP verbs to model perms |
Experienced teams often outgrow default perms and add role tables or external IAM—but know the built-ins for interview baselines.
A strong answer is:
Groups bundle model permissions for RBAC; for fine-grained APIs I combine Django permissions with object-level checks or a dedicated roles model.
Testing, caching, and signals
How do you test Django apps with pytest-django?
What interviewers are testing: Whether you can split tests between fast domain/service tests, database integration tests, API contract tests, and explicit query-count regression checks rather than testing everything through HTTP.
pytest-django provides fixtures and database handling:
import pytest
@pytest.mark.django_db
def test_create_order(api_client, user):
api_client.force_authenticate(user=user)
response = api_client.post("/api/v1/orders/", {"sku": "A1"}, format="json")
assert response.status_code == 201| Tool | Use |
|---|---|
@pytest.mark.django_db |
DB access in test |
factory_boy |
Realistic model factories |
django_assert_num_queries |
Guard against N+1 regressions |
APIClient |
DRF integration without live server |
def test_order_list_queries(django_assert_num_queries):
with django_assert_num_queries(2):
list(Order.objects.select_related("customer"))Prefer fast tests: many unit/service tests, fewer full-stack. Use django_capture_on_commit_callbacks when you need to assert or execute on_commit() callbacks; use transactional tests only when the test genuinely requires transaction behavior.
A strong answer is:
I use pytest-django with factories for data, APIClient for HTTP contracts, and
django_assert_num_querieson hot endpoints to catch ORM regressions.
What is the difference between Django Test Client, DRF APIClient, and live server tests?
What interviewers are testing: Whether you distinguish en django test client, drf apiclient, and live serve... with correct terms and a real usage example.
| Client | Scope |
|---|---|
django.test.Client |
Django views, templates, forms |
rest_framework.test.APIClient |
DRF views, auth helpers, JSON |
LiveServerTestCase / pytest-selenium |
Real HTTP port—slower, E2E |
Most experienced loops want you to test service layer without HTTP when rules are complex—faster and clearer failures.
A strong answer is:
APIClient for HTTP integration, direct service tests for business rules, live server only for true E2E—keep the pyramid mostly fast unit and integration tests.
What caching layers does Django support?
What interviewers are testing: Whether you can explain what caching layers does django support with correct definitions and one concrete example.
| Layer | Mechanism |
|---|---|
| Per-view | @cache_page(timeout) |
| Template fragment | {% cache %} tag |
| Low-level API | cache.get/set with Redis/Memcached backend |
| Per-site | Django cache middleware for caching entire responses |
Django does not provide a transparent general-purpose QuerySet result cache across requests; cache application results explicitly when appropriate.
Invalidation is the hard part:
- Key by object version or TTL
- Prefer explicit invalidation in services over scattered signals
from django.core.cache import cache
def get_dashboard_stats(user_id):
key = f"dashboard:{user_id}"
data = cache.get(key)
if data is None:
data = compute_stats(user_id)
cache.set(key, data, timeout=300)
return dataA strong answer is:
I cache expensive reads in Redis with explicit keys and TTLs, invalidate on writes in the service layer, and measure hit rate—not cache by default without profiling.
When should you use Django signals — and when should you avoid them?
What interviewers are testing: Whether you understand signals as implicit framework hooks and prefer explicit services for business-critical workflows and transaction-aware side effects.
Signals (post_save, pre_delete, …) decouple side effects from save paths.
| Reasonable use | Usually avoid |
|---|---|
| Framework/integration hooks | Core business workflows |
| Loose notifications where explicit coupling is undesirable | Hidden email/task dispatch from arbitrary saves |
| Simple cache invalidation, with transaction semantics considered | Multi-step domain transactions |
Problems signals cause:
- Implicit execution order
- Hard to test and trace
- Signals run synchronously as part of the code path that emits them. If that save occurs inside an atomic transaction, an exception from the receiver can cause that transaction to roll back
Prefer explicit service methods for critical workflows.
A strong answer is:
Signals are for loose coupling at framework boundaries; I avoid them for business logic and use services plus on_commit for side effects I need to reason about in tests.
What are management commands and how do you use them in production?
What interviewers are testing: Whether you can design idempotent, observable batch operations for cron or one-off maintenance without overloading web workers.
Management commands (python manage.py my_command) run admin tasks:
| Use case | Example |
|---|---|
| One-off data fix | Backfill nullable column |
| Scheduled job | Cron/K8s CronJob invoking command |
| Imports | CSV ingest with batching |
Structure with BaseCommand, add_arguments, and idempotent design. Long jobs: log progress, support --dry-run, wrap in transaction.atomic() per batch.
A strong answer is:
Management commands are my tool for batch ops and cron—they are idempotent, log clearly, and batch database work instead of loading everything into memory.
Celery, deployment, and async Django
How does Celery integrate with Django and what production pitfalls appear?
What interviewers are testing: Whether you enqueue work safely after commit, pass stable identifiers, bound retries, and understand how Celery differs from Django's newer Tasks framework.
Celery runs async tasks in worker processes—broker (Redis/RabbitMQ) holds the queue.
@shared_task(bind=True, max_retries=3)
def send_receipt(self, order_id):
try:
order = Order.objects.get(pk=order_id)
mail.send(order)
except TransientMailError as exc:
raise self.retry(exc=exc, countdown=60)| Pitfall | Fix |
|---|---|
| Task before DB commit | Use transaction.on_commit |
| Unbounded retries | max_retries, bounded retries with backoff/jitter, failure handling, and a strategy for permanently failed tasks |
| Long tasks blocking workers | Chunk work; separate queues |
| Passing model instances | Pass IDs only—stale data |
Monitor queue depth, worker memory, and task failure rates.
Django 6.x note: Django now includes a Tasks framework that standardizes task definition and queueing, but it does not provide the production worker mechanism itself. Celery remains relevant when a project needs its worker ecosystem, scheduling, routing, retries, and broker integrations.
A strong answer is:
Celery handles async side effects—I enqueue by ID after commit, cap retries with backoff, and monitor queues so email or report jobs never block web workers.
How do you serve static and media files in production?
What interviewers are testing: Whether you separate static asset delivery from user-uploaded media and avoid serving large media through application workers at scale.
| Type | Dev | Production |
|---|---|---|
| Static (CSS/JS) | runserver or collectstatic |
WhiteNoise, S3, or CDN via collectstatic |
| Media (user uploads) | Local MEDIA_ROOT |
S3/Azure Blob with django-storages |
Never serve user uploads from Django workers at scale—use object storage + signed URLs.
collectstatic in CI/CD before deploy; cache-bust with hashed filenames (ManifestStaticFilesStorage).
A strong answer is:
Static assets go to CDN or WhiteNoise after collectstatic; user media lives in object storage with signed URLs—not on app server disks.
What are the limits of async Django views in 2026?
What interviewers are testing: Whether you understand where Django is genuinely async-capable today, where synchronous boundaries remain—especially transactions—and when ASGI actually improves concurrency.
Django 6.1 has broad async support across views and many framework APIs, including asynchronous QuerySet methods such as aget(), acreate(), and async for. The important remaining boundary is that database transactions are not supported in async mode.
| Works naturally in async code | Important boundary |
|---|---|
| Async HTTP/network I/O | CPU-heavy work still blocks the event loop |
aget(), acreate(), async for and other async ORM APIs |
Don't call synchronous ORM methods directly |
| Async-capable framework APIs | Transactions still require a synchronous function/bridge |
| Streaming/long-lived responses under ASGI | Sync middleware can reduce async concurrency benefits |
A strong answer is:
Async Django is useful when requests spend time waiting on concurrent I/O. In Django 6.1 I use native async ORM/framework APIs where available, but transactional database workflows still stay synchronous and cross into async with
sync_to_async(). I choose ASGI for measured concurrency needs, not because async is automatically faster.
How do you deploy Django with Docker and Gunicorn?
What interviewers are testing: Whether you design images and runtime with security and reproducibility.
Typical container layout:
| Process | Role |
|---|---|
| Gunicorn | Common WSGI process server |
| Uvicorn / another ASGI server | ASGI deployment for async-capable applications |
| Migration job | Run schema migrations once per deploy |
| Celery / task worker | Separate background worker |
| Redis / RabbitMQ | Broker/cache depending on architecture |
| PostgreSQL | Usually managed production database |
# Simplified pattern
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]Health check endpoint (not admin)—wire to load balancer. Run check --deploy in CI.
Pair with Git interview questions for release workflow stories.
A strong answer is:
I containerize web and worker processes separately, run migrations as a deploy step, put Gunicorn behind a reverse proxy, and expose a health check for the load balancer.
Senior scenarios and final prep
Scenario: A DRF list endpoint is slow in production — how do you debug?
What interviewers are testing: Whether you measure query count and SQL time before optimizing serializers or adding caches, and can walk a structured production-debugging path.
| Step | Action |
|---|---|
| 1 | Confirm scope—one endpoint? spike after deploy? |
| 2 | APM or logs—latency breakdown (DB vs serialization) |
| 3 | Query count — django-debug-toolbar in staging, assertNumQueries locally, connection.queries in dev |
| 4 | Check serializer nesting and SerializerMethodField DB hits |
| 5 | Inspect slow SQL with QuerySet.explain() / database EXPLAIN; check indexes and row estimates |
| 6 | Add select_related/prefetch_related or denormalize read model |
| 7 | Pagination—ensure not returning thousands of rows |
| 8 | Cache stable reads if appropriate |
Common root causes: N+1, missing index, unbounded queryset, synchronous external HTTP in serializer.
A strong answer is:
I measure query count and SQL time first, fix ORM fetching and pagination, then cache or denormalize only after profiling proves the bottleneck.
Scenario: Two requests decrement the same inventory — how do you prevent overselling?
What interviewers are testing: Your ordered scenario response—verify first, prove root cause, then prevent recurrence.
Options (often combined):
| Approach | Detail |
|---|---|
select_for_update() |
Lock row inside atomic() during read-modify-write |
F() expression |
update(stock=F('stock') - 1) with filter(stock__gt=0) |
| Database constraint | CheckConstraint(stock__gte=0) |
| Idempotent orders | Unique key on client request id |
updated = Product.objects.filter(pk=sku, stock__gt=0).update(stock=F("stock") - 1)
if updated == 0:
raise OutOfStock()Avoid application-level read-then-write without locking under concurrency.
A strong answer is:
I use atomic updates with F() or row locks, enforce non-negative stock in the database, and return clear errors when the update count is zero.
How do you structure a large Django codebase?
What interviewers are testing: Whether you can organize a growing Django monolith around domain boundaries and explicit dependencies instead of creating giant shared modules or cross-app import tangles.
Common mature layout:
project/
config/ # settings, urls, wsgi
apps/
orders/
models.py
services.py
selectors.py
api/
serializers.py
views.py
urls.py
tests/
users/
common/ # shared utilities, exceptionsPrinciples:
- Apps by bounded context—not by technical layer only
- api/ package per app for DRF surface
- Avoid circular imports—services call repositories/selectors, not views
- Shared exception handler for consistent API errors
A strong answer is:
I split by domain apps with services and API packages inside each, keep settings and URLs centralized, and enforce boundaries so imports flow inward—not views importing across domains chaotically.
Final-week Django interview checklist
Checklist:
- Draw request lifecycle with middleware order
- Explain N+1 fix on a real serializer with
prefetch_related - Walk through
transaction.atomic+on_commit+ Celery - Recite security checklist — CSRF, XSS, DEBUG, ALLOWED_HOSTS,
check --deploy - DRF story — pagination, auth, object permissions, versioning
- One migration war story — backward compatible rollout
- One performance win — query count before/after
- Python fundamentals refresh — decorators, GIL, asyncio limits
- SQL — indexes and EXPLAIN
Behavioral: STAR story for production incident (runaway query, failed task queue, bad deploy).
Pattern cheat sheet (quick reference)
| Need | Django starting point |
|---|---|
| Forward FK join | select_related() |
| Reverse / M2M batch | prefetch_related() |
| Complex OR | Q() objects |
| Atomic counter | F() + filter(stock__gt=0).update() |
| Safe async side effect | transaction.on_commit(task.delay) |
| Row lock | select_for_update() in atomic() |
| API CRUD | DRF ViewSet + router |
| Object authz | Filter queryset + has_object_permission |
| Prod security | DEBUG=False, check --deploy |
| Background work | Celery + Redis broker |
| Tests | pytest-django + assertNumQueries |
References
Official Django documentation
- Django documentation
- Django REST framework
- Django deployment checklist
- Django async support
- Database transactions
Summary
Experienced Django interviews trace the full request pipeline, efficient ORM usage, DRF authorization, and Celery, caching, and migrations in production. Answer aloud and compare your structure to each section. Pair with Python developer interviews and SQL interviews when list endpoints fail at the database layer.

