Django
Django is a batteries-included framework, and its defining trait is not size but the fact that every layer hides real work behind ordinary-looking Python. Book.objects.filter(...) looks like a finished list, but it is a QuerySet that has not touched the database once. class Meta looks like a nested class, but it is configuration read by a metaclass while the model is being built. @receiver(post_save) looks like a background event subscription, but the receiver runs synchronously inside your request and delays the response. Almost every interview mistake is surprise that the hidden machinery did not behave the way the visible form suggested.
That tells you what is actually being probed. Not method names — those can be looked up — but the ability to name the moment something happens and the number of queries it costs. When SQL executes. How many queries a loop over book.author issues. What happens between reading a field in Python and writing it back. In what order DRF runs validation, and why validate(attrs) sometimes never runs at all. The layers below work through these mechanisms one at a time — from framework philosophy to the ORM and on to the request cycle.
Topic map
- Django vs Flask — two philosophies — what "batteries included" buys you, what Flask leaves to you, and how to choose by the task rather than by taste.
- The ORM and the lazy QuerySet — a model as a table description and a
QuerySetas a deferred query; the exact list of moments the SQL finally runs. - The inner Meta class — nested configuration read by a metaclass at class-build time, and why fields never go inside it.
- Relations and the join table —
ForeignKey,OneToOneFieldandManyToManyFieldat the column and table level, plus thethroughmodel. - Abstract base vs multi-table inheritance —
abstract = Truecreates no table and hands out columns; concrete inheritance creates a table and a hiddenJOIN. - N+1, select_related and prefetch_related — where the hundred queries come from, what a
JOINdoes, and what a second query joined in Python does. - F, Q and Case expressions — how to push computation into SQL, why that removes the read-modify-write race, and how to build an arbitrary order.
- Middleware and chain order — nested wrappers around the view, the request and response phases, short-circuiting, and the cost of a wrong order.
- Authentication and permissions — the cookie session, the lazy
request.user, password hashing, groups and permissions. - CSRF — the token and unsafe methods — why GET is exempt, what is actually compared, and why
@csrf_exemptis dangerous. - Signals and implicit control flow — synchronous dispatch, the operations that emit nothing, and why an explicit call is almost always better.
- The DRF serializer and validation order — the four validation steps and the exact place a cross-field rule belongs.
Common mistakes and traps
| Mistake | Consequence |
|---|---|
Thinking Book.objects.filter(...) has already hit the database | A QuerySet is lazy — SQL runs only on iteration, list(), len(), bool(), indexing or repr(); an extra call next to a loop issues a second query instead of reusing the cache |
Using select_related on a ManyToManyField or a reverse ForeignKey | FieldError — a single JOIN can only pull a single-valued relation; multi-valued ones need prefetch_related |
| Reading a field in Python, adding to it, and saving it back | Another process slips in between the read and the write, and one of the increments is lost; F('views') + 1 moves the arithmetic into SQL and removes the race |
Extracting shared fields into a concrete model instead of abstract = True | You get an extra table and an implicit OneToOneField, and every access to an inherited field now costs a JOIN |
Expecting post_save from queryset.update() or bulk_create() | Those operations issue one SQL statement bypassing Model.save(), so receivers stay silent and the "reliable" logic in the signal simply never runs |
Slapping @csrf_exempt on a view to silence a 403 | The check is removed from the whole view, and any third-party site can now submit that form; the real cause is almost always a missing X-CSRFToken header |
Placing AuthenticationMiddleware above SessionMiddleware | ImproperlyConfigured on the very first request — the order in MIDDLEWARE defines wrapper nesting, it is not just a list of settings |
Putting a cross-field check in the serializer's validate_<field> | That method only sees its own field; comparing two fields belongs in validate(attrs), which will not run at all if any single field failed |
What interviews check
Django comes up in almost every middle-level interview, and the conversation slides from the framework to the database fast. It starts simple — "what is an ORM", "what is middleware", "what are signals" — but the real probe is the follow-up: "how many queries does this loop issue?" That is where it shows whether you have shipped production, because someone who has once chased an N+1 in the logs describes select_related and prefetch_related not as definitions but as the difference between a JOIN and a second query joined in Python. The counter-increment task is the same kind of tell — an F() in the answer means the candidate sees the difference between computing in Python and computing in SQL.
Then come the precision questions. On Meta the interviewer checks that you do not confuse configuration with fields or with a base class. On CSRF you are asked why GET is exempt — the right answer is about safe methods being side-effect-free per HTTP semantics, not about speed. The signals question almost always has a hidden second half — are they synchronous, and should you use them; "synchronous, and an explicit call is usually better" scores higher than reciting every built-in signal. The typical failure is identical everywhere — the candidate recites the documentation correctly but cannot name the moment of execution or the cost in queries.