In this article
Django Interview Questions (2026): Senior, Mid & Junior
Tech & Infrastructure
Jun 8, 2026
12 min read
A developer can ace your interview and still take production down six weeks later. They define the ORM cleanly and miss the N+1 query that turns one request into five hundred. This guide gives you Django interview questions for junior, mid, and senior roles, each with what a weak answer reveals and what a production answer sounds like.
A developer aced your interview. They defined the ORM cleanly, explained the request lifecycle, and named the difference between a ForeignKey and a ManyToManyField without hesitating. You hired them.
Six weeks later the order page takes nine seconds to load. One query has become five hundred, because the loop that renders the list hits the database once per row. The developer who passed your interview had never seen this, because it never happens on a laptop with ten test records. It happens in production with a hundred thousand real ones.
Call it the localhost developer: someone whose Django runs perfectly on their own machine and falls apart the moment it meets real traffic. They are not junior. They have shipped features. They have simply never been on call when the database went down, and no definitional interview question will surface that gap.
This guide is built to surface it. Every question below includes what a weak answer sounds like, what a production answer sounds like, and why the gap matters. Use it from either side of the table. If you are hiring, it is a filter. If you are interviewing, it is a map of what separates a Django developer who has operated the framework from one who has only built with it.
The questions are split into three levels: junior, mid-level, and senior. The line between them is not years. It is how much production a developer has survived.
How to read these questions
Seniority is not time served. A developer can repeat junior patterns for five years. Another reaches senior-level judgment in three. The structured-interview research is clear on this: work-sample questions tied to real scenarios predict job performance far better than trivia or unstructured conversation (Schmidt and Hunter, Psychological Bulletin, 1998). Ask what the candidate would do when the system is on fire, not what a decorator is.
A quick map of what each level should reveal:
| Level | Experience | What the questions test |
|---|---|---|
| Junior | 0-2 years | Fundamentals, debugging instinct, willingness to learn |
| Mid | 3-6 years | Production awareness, query optimization, testing discipline |
| Senior | 6+ years | Failure anticipation, concurrency, zero-downtime operations |
If you are building the role itself before you interview, start with our Django developer job description templates, then use the questions below to confirm the requirements you wrote.
Junior Django interview questions
Junior questions test whether the fundamentals are real or memorized. A junior developer is not expected to have production scars. They are expected to think clearly and learn fast.
1. Walk me through what happens when a request hits a Django app
This is the single best opening question for any level. It reveals the candidate’s mental model in ninety seconds.
A weak answer stops at “the URL goes to a view and the view returns a response.” A strong answer traces the full path: the request passes through middleware, the URL dispatcher matches it to a view, the view interacts with models through the ORM, data is passed to a template or serialized to JSON, and the response travels back out through middleware. The detail that separates them is middleware. A developer who never mentions it has never debugged a request that broke before it reached the view.
Why it matters: Every production bug starts somewhere in this chain. A developer who cannot describe the chain cannot debug it.
2. What is the N+1 query problem?
The most important junior-level ORM question. Most candidates have caused an N+1 without knowing its name.
A weak answer does not recognize the term. A strong answer explains it concretely: you fetch a list of objects in one query, then loop over them accessing a related object, and each access fires a new query. Ten blog posts become eleven queries. Ten thousand become ten thousand and one. The strong candidate names the fix without prompting: select_related for single-valued relationships, prefetch_related for multi-valued ones.
| Method | Use for | How it works |
|---|---|---|
select_related | ForeignKey, OneToOne | Single SQL JOIN |
prefetch_related | ManyToMany, reverse FK | Separate queries, joined in Python |
Why it matters: N+1 queries do not appear in local development with ten records. They appear in production with a million, and they take the database down.
3. What is the difference between null=True and blank=True?
A small question that reveals whether a candidate reads carefully or skims.
Candidates who skim treat them as the same flag. They are not. null=True works at the database level and permits a NULL in the column. blank=True works at the validation level and lets the field be left empty in a form. They often appear together, which is exactly why careless developers assume they are interchangeable. They control different layers.
Why it matters: Confusing the two produces forms that reject valid input or databases that store inconsistent state. Precision here predicts precision elsewhere.
4. How would you find out why a Django page is loading slowly?
A junior developer will not have a complete answer. The instinct is what you are testing.
A weak answer guesses: “maybe the server is slow.” A strong answer reaches for evidence: check the number of queries with Django Debug Toolbar, look at which queries repeat, check whether the ORM is loading more data than needed. A junior who knows that a slow page is usually a query problem, not a server problem, is already thinking like an engineer.
Why it matters: The willingness to measure before guessing is the foundation of everything senior. You can teach the tools. You cannot easily teach the instinct.
Mid-level Django interview questions
Mid-level questions test production awareness. The candidate has shipped features that real users touched. They have seen things break. These questions separate developers who write code that works from developers who write code that survives.
5. You have a Django REST Framework endpoint that returns a list of orders, each with its customer and line items. It is slow. What do you do?
This is the N+1 question again, one level deeper, inside a real DRF serializer.
A weak answer adds caching without finding the cause. A strong answer diagnoses first: the serializer accesses related objects per row, producing an N+1 cascade. The fix is to optimize the queryset feeding the serializer with select_related('customer') and prefetch_related('line_items') before it ever reaches serialization. A senior-leaning candidate adds that they would confirm the query count with assertNumQueries in a test so the regression cannot return silently.
Why it matters: DRF makes N+1 problems invisible because the serializer hides the database access. A developer who knows where DRF hides its queries is worth more than one who has memorized serializer syntax.
6. How do you run a task in the background, and what happens when it fails?
Tests whether the candidate understands asynchronous work beyond “use Celery.”
A weak answer stops at “I’d use Celery to run it in the background.” A strong answer keeps going to the failure case: the task needs a retry policy, the retries need backoff so a failing downstream service is not hammered, and the task must be idempotent because a retried task may run more than once. A developer who has operated Celery knows that the naive retry is where the real damage happens.
Why it matters: Background tasks fail constantly in production. Payment webhooks time out, email providers rate-limit, third-party APIs go down. A developer who only knows the happy path will write a task that corrupts data on its second run.
7. When do you use a function-based view versus a class-based view?
Tests judgment, not knowledge. There is no single correct answer, which is the point.
Watch for the candidate who answers with a rigid rule and no reasoning behind it. The useful answer weighs the tradeoff: function-based views stay explicit and readable for simple logic, class-based views cut duplication on standard CRUD through inheritance and mixins, and the price of class-based views is indirection that hides behavior. Their reasoning matters more than which one they prefer.
Why it matters: Senior engineering is a sequence of tradeoff decisions. A developer who holds a position and can defend it with reasoning will make better architecture calls than one who recites a rule.
8. How do you test a Django view that depends on the database?
Tests engineering discipline. Many developers can write code. Fewer write code they can prove works.
A weak answer describes manual testing in the browser. A strong answer describes pytest-django with a test database, fixtures or factories to build state, and assertions on both the response and the resulting database state. The detail that signals discipline: testing behavior, not implementation, so the test survives a refactor.
Why it matters: Code without tests is a liability that compounds. The mistakes that come from skipping this are documented in our breakdown of the most common Python hiring mistakes, where shipping untested code is a recurring failure mode.
Senior Django interview questions
Senior questions test production scars. These are the questions for experienced engineers, the ones a developer can only answer well if they have been on call when the system broke. A candidate who answers these has operated Django at scale. A candidate who cannot has only built with it.
9. You need to add a NOT NULL column to a table with 50 million rows. The app cannot go down. How?
The highest-signal senior question in this entire guide. It separates developers who have run a migration on a live table from developers who have only run them locally.
A weak answer says “run the migration.” That answer reveals the candidate has never done this at scale, because a naive ALTER TABLE adding a NOT NULL column with a volatile default takes an ACCESS EXCLUSIVE lock and rewrites the entire table, blocking every read and write until it finishes. On 50 million rows, that is minutes of total downtime.
A strong answer describes the expand/contract pattern: add the column as nullable first (metadata-only on PostgreSQL 11+, no rewrite), deploy code that writes to it, backfill existing rows in small batches, then add the NOT NULL constraint in a separate step. The candidate knows that schema changes and code changes must ship as independent, backward-compatible deploys. Django’s migration system supports exactly this separation.
Why it matters: This one operation, done wrong, is a company-wide outage during peak traffic. It is the clearest line between a developer who has operated Django and one who has not.
10. Two customers click “buy” on the last item in stock at the same millisecond. How do you stop both orders from going through?
The race condition question. Tests whether the candidate understands that transaction.atomic() alone does not prevent this.
A weak answer wraps the logic in a transaction and assumes it is safe. It is not. Under PostgreSQL’s default READ COMMITTED isolation, both transactions read stock as 1, both pass the check, both decrement. Two units sold, one in stock. The atomic block guaranteed all-or-nothing, not mutual exclusion.
A strong answer reaches for select_for_update() inside the transaction, which locks the row so the second transaction blocks until the first commits, then re-reads the now-zero stock and correctly refuses. A candidate with deep knowledge mentions the alternative: an atomic F() expression update that pushes the check and decrement into a single SQL statement, avoiding the lock entirely. Django’s select_for_update documentation covers the locking behavior.
| Approach | When to use |
|---|---|
select_for_update() | Complex critical section in Python |
F() expression update | Check and update expressible in one query |
Why it matters: Oversell bugs, double charges, and duplicate bookings all live here. They are invisible in testing and catastrophic in production, and they only appear under concurrent load.
11. Your Celery tasks all fail at once when a downstream API goes down, then overwhelm it the moment it recovers. Why, and how do you fix it?
Tests whether the candidate understands the thundering herd problem.
Add a fixed retry delay and you have not solved anything. If every failed task retries after exactly sixty seconds, all of them retry in the same second and knock the recovering service straight back down. The fix is exponential backoff with jitter, so retries scatter randomly across a window instead of firing in lockstep. A strong candidate knows Celery does this natively with retry_backoff and retry_jitter, and that jitter is enabled by default as of Celery 5.3.
Why it matters: Correlated retries turn a brief downstream blip into a sustained outage. This is the kind of failure that only a developer who has watched it happen will design against.
12. What breaks when you put a normal ORM query inside an async Django view?
Tests current, accurate knowledge of Django’s async support, which most candidates get wrong.
A weak answer assumes the ORM is fully async because Django supports async views. It is not. A strong answer is precise: a synchronous ORM call inside an async context raises SynchronousOnlyOperation. The candidate knows Django 5.2 provides a-prefixed async query methods like aget and acreate and supports async for, but that these are thin wrappers, the ORM is not natively non-blocking, and transactions do not work in async mode at all. Transactional work must be wrapped in sync_to_async.
Why it matters: Teams adopt async views expecting a performance win and instead get runtime errors or silent blocking. A senior developer knows the current limits and designs within them instead of assuming the marketing.
13. New API, greenfield project. Django REST Framework or Django Ninja? Defend your choice.
There is no wrong answer. There is a wrong way to answer, which is to have no reasoning.
The answer that tells you nothing is the one that picks a framework because it is the only one the candidate knows. The answer that tells you everything weighs the trade: DRF brings the mature ecosystem, battle-tested viewsets and permissions, and a hiring pool where nearly every Django developer already knows it. Django Ninja runs on Pydantic v2 with a Rust validation core, serializes faster, generates OpenAPI docs with no extra package, and is async-first. The verdict matters less than whether they understand the tradeoff between ecosystem maturity and developer velocity.
Why it matters: This is a real architecture decision your team will live with for years. A developer who can reason about it under questioning will make sound calls when you are not in the room. The deeper signal of seniority here is covered in what separates a senior Python developer from a coder.
14. In a job queue with many workers, how do you stop two workers from grabbing the same job?
A deep concurrency question that separates strong seniors from the rest.
A weak answer does not know the locking tools exist. A strong answer uses select_for_update(skip_locked=True), so each worker locks and claims rows that are not already locked, silently skipping the ones other workers hold. The candidate distinguishes this from nowait=True, which raises an error on a locked row instead of skipping it. One is for job queues, the other is for fail-fast interactive requests.
| Option | Behavior on locked row |
|---|---|
skip_locked=True | Skip it, return unlocked rows (job queues) |
nowait=True | Raise immediately (fail-fast requests) |
Why it matters: Job queues built without this run the same job twice or deadlock under load. It is a small piece of knowledge with a large production blast radius.
The five-minute screen
Before a full technical interview, one question filters most of the field. Ask it on the phone:
“Describe a database migration you ran against a live production table. What did you do to avoid downtime?”
A developer who has done it describes the expand/contract pattern, backward-compatible column additions, or a phased rollout. A developer who has not gives a vague answer about Django’s migration framework. The question takes two minutes and saves you a full interview loop. It pairs naturally with the requirement-by-requirement framework in our senior Python developer evaluation guide.
Stop running the whole loop yourself
Writing the role, screening fifty resumes, and running a full interview loop to land one developer takes weeks. Every week the seat sits empty is a week of features not shipped, and the entire exercise exists to filter out one kind of hire: the localhost developer who looks senior on paper and falls over under load.
There is a shorter path. The questions in this guide are the same screen Meduzzen’s Django developers have already passed: N+1 and ORM optimization, Celery retry design, zero-downtime migrations, concurrency locks, and Django’s async limits. Senior engineers cost $35 an hour through staff augmentation, with no recruiter fee and a developer matched in 48 hours. You interview one pre-vetted candidate instead of twelve unknowns.
If you prefer to hire directly, the questions above are yours to use. Pair them with the Django developer job description templates and you have a complete screening system from job post to final round.
Frequently asked questions
The questions that test production operations: running a zero-downtime migration on a large table, preventing race conditions with select_for_update(), designing Celery retries with jitter, and knowing the limits of Django’s async ORM. These separate developers who have operated Django from those who have only built with it. Definitional questions about the ORM or MVT architecture belong in a junior screen.
Django-specific depth beyond general Python fluency. Expect questions on ORM query optimization, where N+1 problems hide inside DRF serializers, Celery task design and what happens when a task fails, and Django’s migration and security systems. A strong Python background is the floor, not the bar. The interview tests whether the candidate has built and operated real Django systems on top of it.
Test fundamentals and instinct, not scars. Ask them to trace a request through the framework, explain the N+1 problem, and describe how they would investigate a slow page. A junior who measures before guessing and recognizes that a slow page is usually a query problem is thinking like an engineer. You can teach the tools. Hire for the instinct.
Scenario questions tied to real production situations predict performance better than algorithm puzzles. A developer who can walk through preventing an oversell race condition or running a safe migration reveals more than one who can reverse a binary tree. Structured, work-sample style questions are among the strongest predictors of on-the-job performance (Schmidt and Hunter, Psychological Bulletin, 1998). Reserve live coding for a realistic task in the candidate’s own environment, not whiteboard trivia.
Five to eight well-chosen questions reveal more than thirty shallow ones. Pick two fundamentals, two production-awareness questions, and two or three that test failure anticipation at the level you are hiring for. Depth of reasoning on a hard question tells you more than breadth of recall on easy ones.