🎸 Django

Learn Django the Right Way β€” From Basics to Advanced

Build a strong foundation by understanding how Django processes every request. Explore URLs, views, templates, models, middleware, authentication, ORM, REST APIs, deployment, and everything you need to become a confident Django developer.

πŸ“…

Last Updated

May 2026

⏱️

Read Time

17 min

🎯

Level

Beginner β†’ Intermediate

What Is Django, Beyond "a Python Web Framework"

Django is a high-level Python web framework built by a team at the Lawrence Journal-World newspaper in Lawrence, Kansas, and released as open source in July 2005. It was named after jazz guitarist Django Reinhardt. The framework was born under newsroom deadline pressure β€” journalists needed new features shipped in hours, not weeks β€” and that origin explains almost everything about Django's design philosophy: it is deliberately opinionated and comes with far more built in than a minimal framework like Flask.

Django calls itself "the web framework for perfectionists with deadlines," and the phrase that matters more for understanding its architecture is "batteries included." Out of the box you get an ORM, an admin interface, a user authentication system, a templating engine, form handling with built-in CSRF protection, and a migration system β€” all pre-integrated, not bolted on separately. Flask and FastAPI give you a thin core and expect you to pick your own ORM, auth library, and admin tooling; Django hands you an opinionated, working default for all of it.

That trade-off β€” less initial flexibility in exchange for far less setup decision-making β€” is the single thing to understand before writing a line of Django code. Everything else in this guide is really just consequences of that one choice.

MVT, Not MVC β€” And the Difference Actually Matters

Django's own documentation calls its architecture MVT (Model-View-Template) rather than the more familiar MVC. The naming isn't just marketing β€” the roles genuinely map differently. In Django, the Model defines your data (same idea as MVC). The Template is what MVC calls the View β€” the HTML rendering layer. And Django's View is actually what MVC calls the Controller β€” the Python function or class that receives a request and decides what data to fetch and which template to render.

Django TermTraditional MVC EquivalentWhat It Actually Does
ModelModelDefines database schema as Python classes; Django's ORM turns them into SQL
TemplateViewHTML files with Django Template Language tags β€” the presentation layer
ViewControllerPython function/class that processes a request and returns a response
URL dispatcherRouterurls.py maps incoming URL patterns to specific view functions

If you've read Rails or Laravel tutorials before touching Django, this naming mismatch is exactly why a Django "View" felt backwards at first β€” it isn't the HTML-rendering piece at all, it's the logic layer that decides what to render.

The Full Journey: URL Typed β†’ Page Rendered

This is the part that actually explains most "why isn't my view running" and "why is this data stale" confusion. A single browser request passes through a specific, ordered pipeline before HTML ever comes back.

🌐 Browser RequestGET /products/42/
TCP connection
🚦 WSGI/ASGI ServerGunicorn/Uvicorn hands off to Django
request object built
🧱 Middleware Stack (inbound)Security, Session, CSRF, Auth β€” in order
passed inbound
πŸ—ΊοΈ URL Resolverurls.py matches pattern β†’ view function
match found
βš™οΈ View FunctionQueries the ORM, builds context dict
query needed
πŸ—„οΈ Model / ORM LayerProduct.objects.get(id=42) β†’ SQL
queryset returned
πŸ“„ Template EngineRenders context into HTML via product.html
HttpResponse built
🧱 Middleware Stack (outbound)Response headers, session cookie set
outbound processing
πŸ–¨οΈ HTTP ResponseRendered HTML sent to browser

Code Execution Flow β€” from source to output

Middleware order in settings.py's MIDDLEWARE list genuinely matters and is a common source of confusing bugs: it runs top-to-bottom on the way in, then bottom-to-top on the way out. Put AuthenticationMiddleware before something that depends on request.user existing, and it works fine. Get the order wrong, and you'll hit an AttributeError: 'WSGIRequest' object has no attribute 'user' that has nothing to do with your view code at all β€” the actual bug is three lines away in a settings file most beginners never think to check.

The ORM's Best Feature Is Also Its Most Common Trap

Django's ORM lets you write Book.objects.filter(author__country="India") instead of raw SQL, and it's genuinely one of the better ORMs in any language β€” readable, chainable, and it generates reasonably efficient SQL by default. The trap almost every intermediate Django developer hits at least once is the N+1 query problem, and it's sneaky specifically because the code that causes it looks completely innocent.

🐍 Python (views.py)orders_list_n_plus_one.py
# Looks fine. Runs 1 query for the orders...
orders = Order.objects.filter(status='pending')

for order in orders:
    # ...but THIS line runs a fresh query for EVERY single order,
    # because order.customer wasn't fetched upfront.
    # 50 pending orders = 1 + 50 = 51 queries for one page load.
    print(order.customer.email)
🐍 Python (views.py)orders_list_fixed.py
# select_related() does a SQL JOIN upfront for foreign-key relations,
# fetching customer data in the SAME query as the orders.
orders = Order.objects.filter(status='pending').select_related('customer')

for order in orders:
    print(order.customer.email)  # No extra query β€” already loaded

# For many-to-many or reverse foreign-key relations, use prefetch_related()
# instead β€” select_related() only works for forward FK/OneToOne fields.

This isn't a rare edge case β€” it's specifically the kind of bug that passes code review, works fine on your laptop with 8 test rows in the database, and then quietly turns a dashboard page from a 40ms response into an 1,800ms response once a client has a few thousand real orders. Django's debug toolbar (django-debug-toolbar) shows exact query counts per page and is worth installing before you need it, not after a production slowdown ticket.

Migrations β€” Why Django Won't Let You Just Edit the Database

When you change a model β€” add a field, rename a column, add an index β€” Django doesn't touch the database directly. It generates a migration file, a Python script describing exactly how to transform the schema, via python manage.py makemigrations. You then apply it with python manage.py migrate. This two-step separation exists so schema changes are version-controlled, reviewable in a pull request, and reversible.

The error every Django beginner eventually hits, usually in their first week deploying to a shared staging database, is:

Output

django.db.utils.ProgrammingError: relation "orders_order" does not exist LINE 1: SELECT ... FROM "orders_order" WHERE ... ^

This nearly always means one thing: you wrote a model and ran the server, but never ran makemigrations and migrate β€” Django's models exist in Python, but the actual PostgreSQL/MySQL table they describe was never created. The fix is almost always exactly those two commands, in that order, and it's worth checking python manage.py showmigrations first to see which migrations are recorded as applied versus pending before assuming anything more exotic is wrong.

Django vs FastAPI vs Flask β€” An Honest Comparison

This gets asked in nearly every Indian product-company interview for a backend role, and the honest answer isn't "it depends" dressed up politely β€” the three frameworks genuinely optimize for different things.

AspectDjangoFastAPIFlask
PhilosophyBatteries included, opinionatedMinimal core + async-first, typedMinimal core, unopinionated
Built-in ORMYes (Django ORM)No β€” pair with SQLAlchemy/TortoiseNo β€” pair with SQLAlchemy
Admin panelYes, auto-generated from modelsNoNo
Async supportSince 4.1 (2022), improving each releaseNative, async-first from day oneLimited, bolted on
Auto API docsNo (needs Django REST Framework + drf-spectacular)Yes, built-in (Swagger/OpenAPI)No
Best fitContent-heavy sites, internal tools, CMS-style appsHigh-throughput APIs, microservicesSmall services, prototypes, learning
Time to first working CRUD appFast, once you accept its conventionsFast for APIs specificallyFast but you assemble more yourself

My honest take: for an internal admin tool or a content-driven product where you'll spend real time in the admin panel managing data, I'd pick Django every time β€” the auto-generated admin alone saves days of throwaway CRUD-screen work. For a pure JSON API backing a mobile app or a microservice under genuine throughput pressure, FastAPI's native async handling and automatic OpenAPI docs usually win, and Django REST Framework on top of Django starts to feel like reassembling what FastAPI gives you natively. Flask's niche has narrowed the most of the three β€” it's still excellent for small, focused services, but FastAPI has taken a lot of the "lightweight and modern" ground Flask used to own alone.

Where Django's "Batteries Included" Actually Pays Off

πŸ› οΈ
Auto-Generated Admin Panel

Register a model with admin.site.register(Product) and you get a full working CRUD interface β€” search, filters, inline editing β€” with zero HTML written. For internal tools and content management, this alone can save a genuine week or two of throwaway front-end work.

πŸ—„οΈ
The ORM

Model changes become Python objects, not raw SQL strings. Combined with the migration system, schema evolution is trackable in git and reviewable like any other code change.

πŸ”
Security Defaults Baked In

CSRF protection, XSS-escaping in templates by default, clickjacking protection via X-Frame-Options, and SQL injection protection through parameterized queries are all on by default β€” not opt-in extras a busy team might skip.

πŸ‘€
Built-In Authentication

User model, login/logout views, password hashing (PBKDF2 by default, configurable to Argon2), permission and group systems β€” ready to use without picking and wiring together a third-party auth library.

πŸ“
Forms & Validation

Django Forms handle rendering, server-side validation, and CSRF tokens together, keeping validation logic in one place instead of scattered between client-side JS and ad hoc server checks.

🧩
Reusable App Structure

Django encourages splitting a project into self-contained 'apps' (accounts, orders, catalog) that can, in principle, be lifted into another project β€” a structural discipline many minimal frameworks leave entirely up to the team.

🌍
Internationalization Out of the Box

Built-in i18n/l10n support with translation files (.po/.mo) and locale-aware formatting is genuinely useful for products targeting multiple Indian language markets without bolting on a separate library.

πŸ“¦
Massive, Mature Package Ecosystem

Django REST Framework for APIs, django-allauth for social login, celery integration for background jobs, django-storages for S3/cloud file storage β€” the ecosystem around Django is unusually mature for a framework its age.

Your First Django View

Unlike a single-file Flask app, a minimal Django project always has at least three moving pieces wired together: a URL pattern, a view function, and the project's urls.py routing table.

🐍 Python (views.py)views.py
from django.http import HttpResponse

def greet_visitor(request):
    return HttpResponse("Hello, World!")
🐍 Python (urls.py)urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello/', views.greet_visitor, name='greet-visitor'),
]

Output

$ python manage.py runserver Visiting http://127.0.0.1:8000/hello/ shows: Hello, World!

Practice This Code β€” Live Editor

The Trade-Offs, Stated Plainly

βœ… Advantages
Extremely Fast to Build Standard CRUD AppsModel, admin registration, and a form together get a working create-read-update-delete interface running in under an hour for straightforward data β€” hard to match without Django's specific combination of pieces.
Security Defaults Reduce Common MistakesCSRF, XSS escaping, and SQL injection protection being on by default means a rushed junior developer under deadline pressure is far less likely to accidentally ship a critical vulnerability than in a bare-metal framework.
The ORM Scales Well With Disciplineselect_related/prefetch_related, database indexes via Meta.indexes, and query annotation cover the vast majority of real-world performance needs without dropping to raw SQL.
Enormous, Stable EcosystemDjango REST Framework alone has effectively become the default way to build REST APIs in the Python world, with a plugin ecosystem built around it spanning a decade-plus.
Excellent DocumentationDjango's official docs are frequently held up as a model for other open-source projects to follow β€” thorough, example-driven, and kept genuinely current with each release.
❌ Disadvantages
Monolithic by DefaultDjango nudges you toward a single deployable application. Splitting a Django project into microservices later is more friction than starting with a framework designed API-first from day one.
ORM Abstraction Can Hide Expensive QueriesThe N+1 problem covered earlier is exactly this risk in practice β€” the ORM's readability can mask what's actually happening at the database level until it becomes a production performance issue.
Async Support Is Still MaturingDjango gained async views in 4.1, but large parts of the ORM remain effectively synchronous under the hood, so a fully async Django stack still has rough edges compared to a framework built async-first.
Steeper Initial Learning Curve Than FlaskUnderstanding the full request-response pipeline, app structure conventions, and the ORM's query API takes longer to absorb than Flask's much smaller surface area.
Can Feel Heavy for Tiny ProjectsSpinning up Django's full project structure for a 50-line internal script or a single-endpoint microservice is often more scaffolding than the task actually needs.

Companies Actually Running Django in Production

  • β–Ά

    πŸ“Έ Instagram β€” historically ran (and largely still runs) on Django at extraordinary scale, and has been a major upstream contributor of async-related performance work back to the framework itself.

  • β–Ά

    🎧 Spotify β€” uses Django for parts of its internal tooling and backend services, valuing the speed of building reliable internal admin and data-management tools.

  • β–Ά

    🧳 Disqus & Bitbucket β€” both built core parts of their platforms on Django during periods of very rapid user growth, leaning on its batteries-included stability under scale pressure.

  • β–Ά

    🏦 Indian Fintech & Edtech Backends β€” a large share of Series A–C Indian startups (lending platforms, D2C-adjacent SaaS, edtech content platforms) default to Django + DRF for their core backend specifically because a small team can ship a secure, admin-manageable product fast without hiring a dedicated DevOps-heavy platform team early on.

  • β–Ά

    πŸ›οΈ Government & Public Sector Portalsβ€” Django's built-in security defaults and mature admin tooling make it a common choice for public-facing government portals and internal case-management systems where auditability and a reviewable admin trail matter as much as raw performance.

Django Interview Questions Companies Actually Ask

Practice Questions

1. A view runs Order.objects.filter(status='shipped') and then loops over the result printing order.customer.name for each order. Django Debug Toolbar shows 41 queries for 40 orders. What's happening, and how do you fix it?

Medium

2. You add a new field to a model and immediately run the server. It throws django.db.utils.ProgrammingError: relation "blog_post" does not exist. What did you skip?

Easy

3. Why does Django hash passwords with PBKDF2 (or Argon2) by default instead of storing them with a simple SHA-256 hash?

Medium

4. What's the practical difference between get_object_or_404() and Model.objects.get() in a view?

Easy

5. A junior developer disables CSRF protection on a POST endpoint using @csrf_exempt because 'it kept failing in Postman.' What's actually going wrong, and is disabling it the right fix?

Hard

6. Why might grid... β€” actually, why does Django recommend prefetch_related() instead of select_related() for a ManyToManyField?

Hard

Is Django the Right Call for Your Next Project?

The question worth asking isn't "is Django good" β€” at this point, with two decades of production hardening behind it, that's settled. The real question is whether your project's shape matches what Django optimizes for: a data-heavy application where an admin interface, a relational database, and server-rendered or lightly API-driven pages are the core of the product. If that describes what you're building, Django will very likely get you to a secure, working product faster than assembling the equivalent from smaller pieces yourself.

Your SituationShould You Reach for Django?
Content-heavy site or internal admin toolβœ… Yes β€” the admin panel alone is a major head start
Startup MVP needing to ship fast with a small teamβœ… Yes β€” batteries-included means fewer early decisions
High-throughput async API / real-time service⚠️ Consider FastAPI β€” async support is improving but not yet as native
Tiny single-purpose microservice⚠️ Flask or FastAPI will likely feel lighter
Team already fluent in Python wanting a proven, stable stackβœ… Yes β€” Django's stability track record is genuinely strong

Whatever you decide, the request-response pipeline covered earlier β€” middleware, URL resolution, views, ORM, templates β€” is the mental model that makes every future Django bug report readable instead of mysterious. Learn that pipeline properly before memorizing shortcuts, and most of what looks like 'Django being unpredictable' later turns out to be one specific, findable step in that chain.

Frequently Asked Questions (FAQ)