84 Django interview questions with detailed answers, sorted from basic to advanced. Free to practice, with progress saved automatically.
Django is a high-level Python web framework that ships with an ORM, authentication, an admin interface, and a template engine out of the box. This section covers models, the ORM and migrations, function-based versus class-based views, Django REST Framework, templates and forms, middleware, signals, WSGI versus ASGI, and query performance, security, and deployment.
and 14 more topics.
Django is a "batteries-included" Python web framework — ORM, admin, auth, forms, templates, migrations all bundled. The philosophy is to let a CRUD/admin-heavy team ship a production-ready product without stitching 5–7 libraries together. Choose Django when you need: server-rendered apps with many admin pages, projects…
Django uses MVT (Model–View–Template) — same core as MVC but renamed to match real roles. Model = table + business logic, View = function/class that takes a request and returns a response (the MVC Controller), Template = HTML file that renders data (the MVC View).…
Project is the whole website (one settings, one deployment); app is a reusable business module inside that project. A project contains multiple apps, each with its own models.py, views.py, urls.py, migrations/. A shop example: project mystore holds apps accounts (user/profile), catalog (product/category), orders (cart/checkout), payments…
The common pattern is to split settings into a package per environment, read secrets from environment variables, never commit real .env files: Choose settings at runtime: DJANGOSETTINGSMODULE=config.settings.prod. Secrets (SECRETKEY, DATABASEURL, API keys) come from os.environ or django-environ. Note: Do not from .dev import inside prod.py…
manage.py is a CLI wrapper around django-admin — it pre-sets DJANGOSETTINGSMODULE for the current project, so python manage.py <cmd always picks up the right dev/test/prod config. Common commands: Two things people forget: runserver is dev-only (production uses Gunicorn/Uvicorn), and generated migration files must be committed…
Model is a Python class extending models.Model, defining table + fields + relations. Migration is a Python file recording schema changes so Django knows what ALTER TABLE to run on migrate. Basic flow: After changing a model (add field, change type), run makemigrations again to…
Lazy QuerySet = Django does not run SQL when you build the query, only when it is evaluated (iteration, list(), len(), bool(), stepped slicing, serialization). That is why you can chain .filter().exclude().orderby() and still issue a single SQL statement. Common bug: calling qs.count() first then…
Both fight N+1 queries, but for different relation kinds: - selectrelated(...) is for ForeignKey / OneToOneField (single relations). Django adds a JOIN to one SQL statement and returns the object with its related row preloaded. - prefetchrelated(...) is for ManyToMany and reverse ForeignKey (multiple relations).…
transaction.atomic() wraps a block of code in a transaction — all or nothing. If anything inside raises, every INSERT/UPDATE/DELETE already executed in the block is rolled back. Use it when several business steps must stay consistent: create an order + decrement stock + write an…
All three live around a model's class Meta, but they solve three different problems: Meta configures the model and its table; abstract = True shares fields without creating a table for the parent; proxy = True changes Python behaviour on the parent's very same table.…
FBV (Function-Based View) is a function taking request and returning HttpResponse — readable end-to-end. CBV (Class-Based View) is a class extending View or a generic class (ListView, DetailView...), organized by HTTP method (get(), post()) and allowing mixins to share logic. Both produce the same result.…
On each request, Django matches request.path against urlpatterns in order, using path() or repath(). At the first pattern that matches, Django stops, calls that pattern's view and passes the parts captured from the URL (such as <slug:slug) to the view as keyword arguments. Each app…
Generic CBVs bundle the standard CRUD pattern: fetch object/list, render template, handle form, redirect — you override only the bits that actually differ. ListView handles pagination, DetailView handles pk/slug lookup, CreateView/UpdateView handle form + save + redirect. Note: When the logic does not fit the…
These three decorators sit on FBVs and reject requests before the logic runs — equivalent to mixins on CBVs (LoginRequiredMixin, PermissionRequiredMixin). - @loginrequired → unauthenticated requests redirect to LOGINURL. - @permissionrequired('app.codename', raiseexception=True) → missing perm raises PermissionDenied (403) instead of redirecting. - @requirehttpmethods(['POST']) → any…
All three are responses returned from a view; they differ in how the body is held and what kind of data they carry. - HttpResponse: builds the whole body in RAM and sends it at once, with Content-Length. Use it for HTML, text, small files.…
Django Template Language (DTL) is the default engine, safe by design: it does not let you call arbitrary functions from a template (only simple arg passing), auto-escapes HTML, uses {{ var }} / {% tag %} syntax. Jinja2 is more flexible: it can call methods…
Form declares fields manually, not tied to a model — fits search, filters, contact forms, multi-step wizards. ModelForm generates fields from a model, ships with save() to write to the DB — fits direct CRUD for one model. Use Form when input does not map…
CSRF (Cross-Site Request Forgery) is when a malicious site tricks the user's browser (already logged into your site) into sending a state-changing request. How it is blocked: the server issues a random secret and stores it in the csrftoken cookie (it only goes into the…
When form.isvalid() is called, Django runs validation in three tiers: - Tier 1 (per field): topython() → built-in field validators (maxlength, EmailValidator...) → custom validators in validators=[...]. - Tier 2 (clean<fieldname()): a hand-written method cleaning one field; it reads the value via self.cleaneddata['<field'], raises ValidationError…
Middleware is a callable that wraps the request → view → response pipeline, running before/after the view for every request. It fits cross-cutting concerns: auth, session, CSRF, logging, GZip, security headers, tenant context. Register it in the MIDDLEWARE setting. Order matters: the request passes through…
Picture the request entering a stack of middleware and unwinding back: In code: the server calls application(request) → MIDDLEWARE[0] → MIDDLEWARE[1] → ... → URL resolve → view → response → middleware unwinds in reverse. If an exception fires inside the view → Django walks…
The order in MIDDLEWARE is the wrapping order: requests travel top to bottom, responses travel back bottom to top. The first entry runs first on the way in and last on the way out. Each middleware only sees what the middleware above it has attached…
django.contrib.auth is the built-in auth module: the User model, password hashing (PBKDF2 by default), session-based login/logout, the @loginrequired decorator, a permission system (group, permission), the {% if user.isauthenticated %} template tag, and ready-made views for login/logout/password reset. Sessions live in the DB (djangosession table) or…
Both let you replace the default auth.User, but the amount of work differs: - AbstractUser = keeps the User schema (username, firstname, lastname, email, isstaff, isactive...), you only add fields or tweak slightly. Use it when username + bundled admin still fit. - AbstractBaseUser =…
Django's built-in is model-level permission only (action X on all of model Y) — no object-level out of the box. - Permission: each model auto-generates 4 perms at migrate: addpost, changepost, deletepost, viewpost. Add custom ones via Meta.permissions = [('publishpost', 'Can publish post')]. - Group:…
DRF is a library on top of Django dedicated to REST APIs: serializers (validate + parse JSON), viewsets (bundled CRUD logic), routers (auto-generated URLs), authentication/permission/throttling/pagination, plus a browsable API UI for testing. Without DRF you can still return JsonResponse from a Django view — but…
Serializer declares fields manually — use it when input/output does not map 1-to-1 to a model (multi-source, computed fields, non-entity actions). ModelSerializer generates fields from a model and ships with create(), update() — use it for standard CRUD. Validation runs in order: built-in field validators…
ViewSet bundles CRUD logic into one class, each method maps to one action (list, retrieve, create, update, partialupdate, destroy). Router reads the viewset and auto-generates URLs and matching names. Note: When you need per-action querysets (e.g. list filtered by user, retrieve open to admins), override…
DRF runs two layers before the handler: Authentication decides who you are (sets request.user), Permission decides whether you may perform this action. Both run inside APIView.initial(): authentication first, then checkpermissions(). The order in which DRF calls permissions: - haspermission() runs on every request (list, create,…
DRF ships three pagination styles. The difference is how each one locates a page in the database, and that decides whether it stays fast on large tables. PageNumberPagination LimitOffsetPagination CursorPagination ------------ Query string ?page=3 ?limit=20&offset=40 ?cursor=cD0yMDI2... SQL issued COUNT() + LIMIT/OFFSET COUNT() + LIMIT/OFFSET WHERE…
Signals are Django's observer mechanism: a sender fires an event (e.g. the Post model was just saved) and every registered receiver function is called. Receivers run synchronously, on the same thread and inside the save()/delete() call, not in the background. Post.objects.create(...) calls save(), Django inserts…
Celery is a task queue running outside the request cycle — the handler enqueues a task on a broker (Redis/RabbitMQ), and worker processes take it off the queue and run it. Use it for email, image thumbnails, export, third-party API sync. Run a worker: celery…
N+1 happens when you run one query to get N objects, then for each object run another query to fetch a related row → 1 + N total queries. Fix with selectrelated (FK/OneToOne, folded into one JOIN) or prefetchrelated (M2M, reverse FK, one extra query…
Two ways to declare them: Index columns that often appear in WHERE, JOIN, ORDER BY. Composite indexes work left-to-right — (author, createdat) serves WHERE author=? ORDER BY createdat, but does not help WHERE createdat=? alone. Note: Indexes are not free — each one slows INSERT/UPDATE/DELETE…
Django offers four cache levels, from coarse-wide to fine-narrow: - Per-site cache — UpdateCacheMiddleware + FetchFromCacheMiddleware cache the whole page response. Fits read-heavy sites like blogs/landing pages. - Per-view cache — @cachepage(60 15) decorator or cachepage() on the URL. Caches one specific view. - Template…
The ORM uses parameterized queries by default — values are never string-formatted into SQL, they are sent separately through the driver: Leaks still happen when: Do it properly: Note: orderby(userinput) in the ORM cannot inject SQL (an unknown field name raises FieldError), but users can…
These are the two interfaces between a Django app and the web server: - WSGI (Web Server Gateway Interface): the synchronous interface; servers are Gunicorn or uWSGI. Each request holds a worker thread/process until it finishes. Simple, stable, Django's default. - ASGI (Asynchronous Server Gateway…
Gunicorn runs one master process that manages several worker processes; each worker loads its own copy of the Django app and handles requests. Configuring it well means picking the right worker type and count for the workload, then measuring and adjusting. Three common worker types:…
Looping instance.save() for 10k rows = 10k INSERT statements, each one a round-trip to the database. The main cost is the round-trip per statement, not the write itself. Bulk operations collapse that into a handful of SQL statements, so the number of round-trips drops sharply.…
All four reduce the data pulled from the DB, used when you do not need the full object: - .values('id', 'name') — returns a QuerySet of dicts, only the listed fields. No model instance is constructed → fast, lean on RAM. - .valueslist('id', 'name') —…
These four are the most-used view helpers, keeping code short and consistent: - getobjector404(Model, lookup) — calls .get(), raises Http404 on DoesNotExist → Django returns the 404 page. Saves repetitive try/except. - Http404 — built-in exception; raising it anywhere lets Django intercept → render 404.html…
Django has three main test base classes, and they differ in how the database is cleaned between tests. Client is a fake browser: it sends requests through the full middleware and URL stack to your views without running a server. - TestCase: wraps each test…
DRF has three standard filter backends; enable them in DEFAULTFILTERBACKENDS and declare on the viewset — no need to hand-roll query param logic. For complex filters (ranges, custom lookups) → use a dedicated FilterSet class: Note: SearchFilter defaults to icontains on the listed fields →…
DRF supports five strategies that differ in where the version lives in the request: Note: Do not version just because something might change later — version only on real breaking changes. A more popular strategy: keep old fields + add new fields + send a…
CORS (Cross-Origin Resource Sharing) is the browser mechanism that makes a server explicitly allow pages from another origin to read API responses. When an SPA at app.example.com fetches api.example.com, the browser checks the Access-Control-Allow- response headers. If the request is not a "simple" one (it…
Beyond DEBUG = False + ALLOWEDHOSTS (see [[9244]]), Django has a SECURE group that must be on for production. Run python manage.py check --deploy and Django lists what is missing. Note: HSTS preload is a long-term commitment — set it wrong and the site is…
Admin is one of Django's most valuable "free" features — non-devs can enter data and audit right after migrate. Customize via a ModelAdmin class registered per model. Common pieces: - listdisplay — columns on the list page; def totaldisplay(self, obj): ... methods also work. -…
- filter(kwargs): returns a QuerySet (0..n rows), lazy (SQL runs only when iterated), and chainable. - get(kwargs): returns exactly one object. None → raises DoesNotExist; more than one → raises MultipleObjectsReturned. Use get() when you are sure the query yields one row by a unique…
They live at two independent layers: - null=True: database layer — the column may store NULL. - blank=True: validation layer (forms/admin) — the field may be left empty on input. Django's convention: - Text/char fields: prefer blank=True without null=True. Django recommends the empty value be…
Choose by the nature of the relationship: - ForeignKey — many-to-one: many rows point to one. E.g. many Orders belong to one User. The key column sits on the "many" side. - OneToOneField — one-to-one, at most one on each side. E.g. Profile extends User.…
Django calls its pattern MVT (Model–View–Template), also written MTV. It resembles MVC in separating data, handling, and presentation, but the names do not line up, and that is where newcomers get confused. Django Classic MVC Role --------- Model Model data + business logic Template View…
A function that takes request and returns a dict; that dict is merged into the context of every template rendered via RequestContext. You register it in TEMPLATES['OPTIONS']['contextprocessors']. Use it to inject global variables into all templates without passing them manually from each view — the…
A parent template (base.html) defines the shared skeleton with placeholders via {% block name %}{% endblock %}. A child template starts with {% extends "base.html" %} and then overrides individual blocks. Blocks the child does not define keep the parent's default content. Inside a child…
aggregate() collapses the whole QuerySet into a single dict; annotate() adds a computed column to each row and still returns a QuerySet. In SQL terms: aggregate() produces a SELECT AVG(...) with no GROUP BY; annotate() produces a JOIN plus a GROUP BY on the base…
Pick based on what you will do with the data next, because a QuerySet only hits the database when evaluated and caches its result after the first evaluation. - Only need a yes/no → qs.exists(). It emits SELECT 1 ... LIMIT 1 and pulls no…
Pass the id (primary key) and let the task re-query the row. Task arguments travel through the broker, so they are serialized (JSON by default). A model instance is neither JSON-serializable nor safe: it is a snapshot of the data at dispatch time, and by…
They are three stacked levels of abstraction; pick by how closely the endpoint matches standard CRUD. - APIView — closest to a plain view: you write get(), post() yourself. Use it when the endpoint is not CRUD over a model: /auth/login, /reports/export, endpoints that call…
No — not without a unique constraint at the database level. getorcreate() is essentially try get → except DoesNotExist → create. Two concurrent requests can both miss on get and both create, producing duplicate rows. Django relies on the database as the arbiter: if the…
Use the Prefetch object to supply the child QuerySet instead of a plain string. prefetchrelated('comments') always pulls every comment. If you only need approved ones, newest first: Three arguments to know: - lookup — the relation name, nested lookups allowed ('commentsauthor'). - queryset — the…
When serializer.isvalid() is called, DRF runs these steps in order: 1. tointernalvalue() — walks the fields one by one. For each field it runs, in order: type coercion and field-level validators (maxlength, UniqueValidator, ...), then immediately the field's own validate<fieldname(self, value). That method does per-field…
Because postsave fires right after save() returns, but before the transaction commits. The Celery worker is a separate process on a separate connection, so its SELECT runs while the data still lives inside an uncommitted transaction — the result is DoesNotExist, or worse, a stale…
A data migration uses the RunPython operation and must fetch models through apps.getmodel(), not by importing from models.py. Why not import directly: a migration runs against the table as it existed at that point in history, while models.py is always the latest version. If you…
Three strategies, chosen by how much staleness you can tolerate. 1. Short TTL (simplest) — accept data that is a few dozen seconds old and skip invalidation entirely. 2. Delete the key on write — the most accurate, but you must remember every write path.…
Retries must come with idempotency, because Celery is at-least-once by default: a worker dying mid-task, or ackslate being enabled, both lead to a task running again. Controlled retries: - Retry only transient errors (timeouts, 5xx, connection loss). Business errors (card declined, malformed payload) will never…