Django Interview Questions

Real Django interview questions on framework trade-offs, migrations, views, DRF, auth, and deployment.

A curated set of Django interview questions, ordered roughly from fundamentals to more advanced — the kind you'll actually be asked in real screens and on-sites.

Philosophy & fundamentals

Q: How do you decide between Django and Flask/FastAPI for a new project? Django is the strongest default when a project needs several of its built-in pieces at once — an ORM, an admin panel, authentication, server-rendered templating — and benefits from one consistent, opinionated structure across a team. Flask and FastAPI fit better when the project is a smaller API, wants full control over which pieces to assemble, or is API-first with no need for server-rendered pages or an admin UI at all. The trade-off is really flexibility versus built-in productivity, not raw capability — all three can build the same application.

Q: What's the difference between a Django project and a Django app? A project is the entire site's configuration — settings, root URL routing, and the collection of installed apps. An app is a focused, in-principle-reusable unit of functionality (a blog, a polls feature) that lives inside a project and must be registered in INSTALLED_APPS before Django picks up its models, admin registrations, or templates.

The ORM & migrations

Q: Describe the migrations workflow, and why it exists. Django tracks schema changes as a versioned sequence of migration files rather than mutating the database directly from model changes. After editing models.py, python manage.py makemigrations generates a migration file describing the change, and python manage.py migrate actually applies pending migrations to the configured database. This gives every environment — a teammate's machine, staging, production — a reproducible, ordered history of schema changes that can be reviewed in code review and rolled forward (or investigated) like any other code change.

Q: What's the difference between .get(), .filter(), and .all() on a Django QuerySet? .all() returns every row as a QuerySet. .filter() returns a QuerySet narrowed to rows matching the given conditions — it can return zero, one, or many rows, and is safe to call even if nothing matches. .get() expects to find exactly one matching row — it raises Model.DoesNotExist if none match and Model.MultipleObjectsReturned if more than one does, so it's only appropriate when "exactly one" is a real invariant, such as looking up by primary key.

Views & URLs

Q: Function-based views vs class-based views — what's the real trade-off? Function-based views are plain functions, which tends to read more clearly for unusual, one-off request-handling logic where everything is linear in one place. Class-based views — especially Django's generic views like ListView, DetailView, and CreateView — remove real boilerplate for standard, repetitive CRUD patterns by providing default behavior you only need to override in specific methods. Neither is a universal answer; many production codebases mix both depending on how well a given view fits the generic-view shape.

The admin site

Q: What makes Django's admin site valuable enough that it's considered a headline feature? Registering a model with admin.site.register() (or a customized ModelAdmin) instantly produces a complete, working CRUD interface — list views with search and filters, validated edit forms, delete actions — with no custom UI code written at all. For internal tools and content-management needs, this can remove the need to build and maintain a separate admin dashboard entirely, and unlike a hand-built UI, it never silently falls out of sync with the model it's generated from.

Django REST Framework

Q: What does DRF's ModelSerializer give you that a hand-written Serializer doesn't? ModelSerializer inspects a model's own field definitions and generates matching serializer fields, validation, and basic (de)serialization logic automatically — the same relationship a ModelForm has to a plain Form. A hand-written Serializer requires spelling out every field and its validation by hand, and keeping that definition in sync manually every time the underlying model changes.

Q: How does a DRF router remove boilerplate compared to registering ViewSet actions with path() directly? A ModelViewSet already bundles list/retrieve/create/update/destroy behavior into one class; a router (like DefaultRouter) then inspects that ViewSet and generates the full, conventional set of URLs for it — GET/POST /resource/ and GET/PUT/PATCH/DELETE /resource/{id}/ — from a single router.register() call. Wiring the same actions by hand with path() would mean writing out each URL and its HTTP method mapping individually, and re-doing it for every new ViewSet.

Authentication, permissions & testing

Q: How do Django's session-based authentication and DRF's permission system fit together on the same project? They operate at different layers and aren't mutually exclusive: Django's login()/logout() and @login_required protect ordinary server-rendered views using session cookies, while DRF's permission_classes (IsAuthenticated, a custom BasePermission, and similar) protect API endpoints, checked against request.user regardless of whether that user was authenticated via session or a token scheme. A project serving both an HTML frontend and a JSON API commonly uses both mechanisms side by side, each securing its own set of views.

Q: Why does Django's TestCase roll back a transaction after every test instead of deleting rows explicitly? Wrapping each test in a transaction that's rolled back afterward is both faster and more reliable than manual cleanup — there's no risk of a test forgetting to delete something it created, and the rollback undoes every change (inserts, updates, deletes) in one operation instead of requiring the test to enumerate what to clean up. It's what lets setUp() freely create fixture data for every test in a class without that data ever leaking into the next test or the next test run.

Deployment

Q: Besides DEBUG = False, what does a Django app need configured before it's safe to deploy? ALLOWED_HOSTS has to list the real domain(s) the app serves, since Django only enforces that allow-list when debug mode is off. Static files need to be collected (collectstatic) and served by something other than Django's own dev-server static handling — commonly WhiteNoise or a dedicated web server like Nginx. And the app needs to run under a real WSGI server (Gunicorn or uWSGI), typically placed behind Nginx for TLS termination and static file serving, rather than continuing to use manage.py runserver.

Q: Why put Nginx in front of Gunicorn instead of exposing Gunicorn directly to the internet? Nginx handles jobs an application server isn't built for: terminating TLS, serving static and media files directly from disk far more efficiently than proxying them through Python, buffering slow client connections, and load-balancing across multiple Gunicorn worker processes or machines. Gunicorn's role narrows to just running the Django application code — splitting these concerns is standard practice rather than asking one layer to do both jobs adequately.