Filter concepts by levelShowing all levels.

Django · Core concepts

Core concepts

The MTV pattern, the request/response pipeline, and the batteries Django ships with

  • Model → View → Templatedata, logic, and presentation stay separate
  • urlpatterns routes first matchchecked top to bottom
  • A model is a classmigrate turns it into a real table
  • Sessions + auth ship togetherrequest.user and request.session, always available

1.What Django is

Django is a Python web framework: tools and conventions for turning an HTTP request into a response, backed by a database, without wiring the plumbing yourself. Install with pip, then build on top of it.

Key features

  • Django is a Python package, installed with `pip install django`
  • It turns an HTTP request into an HTTP response through a fixed, well-defined pipeline
  • `django-admin startproject` scaffolds a runnable project in seconds
  • It ships an ORM, an admin site, auth, sessions, and a template engine by default
  • One project can hold many reusable "apps" — see project vs. application
What Django sits between

HTTP request

from a browser or client

Django

routes, queries, renders

Database

via the ORM

HTTP response

HTML, JSON, a redirect

  1. HTTP request — from a browser or client
  2. Django — routes, queries, renders
  3. Database — via the ORM
  4. HTTP response — HTML, JSON, a redirect

Command

bash
pip install django
django-admin startproject mysite
cd mysite
python manage.py runserver

Django is a full Python web framework — routing, ORM, admin, auth, and templates all included and wired together, not a library you assemble piece by piece.

2.MTV architecture

Django splits an app into Models (data and rules), Templates (how output looks), and Views (logic connecting a request to a model and a template) — its own name for a pattern close to MVC.

Key features

  • Model = data and business rules — a Python class mapping to a database table
  • Template = presentation — HTML with placeholders, no business logic
  • View = the glue — a function/class reading a request, using a Model, choosing a Template
  • Django's own docs call this "MTV" but note it is very close to the well-known MVC pattern
  • Confusingly, Django's "View" plays the role MVC calls the "Controller"

MTV compared with the more familiar MVC naming

MTV compared with the more familiar MVC naming
Django (MTV)Traditional MVCResponsibility
ModelModeldata shape and business rules
TemplateViewpresentation only, no logic
ViewControllerreads the request, talks to the Model, picks a Template
(the framework itself)ControllerDjango's URL dispatcher does some of what a Controller does too
How a request flows through Model, View, Template
querydatacontext

HTTP request

View

reads request, calls Model

Model

data + rules

Template

renders HTML

HTTP response

  • HTTP request
    • leads to View
  • View — reads request, calls Model
    • leads to Model (query)
    • leads to Template (context)
  • Model — data + rules
    • leads to View (data)
  • Template — renders HTML
    • leads to HTTP response
  • HTTP response

Command

python
# Model — models.py
class Article(models.Model):
    title = models.CharField(max_length=200)

# View — views.py
def article_detail(request, pk):
    article = Article.objects.get(pk=pk)
    return render(request, "article.html", {"article": article})

Model = data/rules, Template = presentation only, View = the glue — Django's View plays MVC's "Controller" role.

3.URL configuration

A URLconf maps URL patterns to views. ROOT_URLCONF points at the module holding `urlpatterns`; `path()` defines one pattern, and `include()` lets an app plug its own URLconf in under a prefix.

Key features

  • ROOT_URLCONF names the module Django loads first, e.g. "mysite.urls"
  • That module must define urlpatterns, a list of path()/re_path() entries
  • path("articles/<int:year>/", view) captures a URL segment as a typed argument
  • include("app.urls") lets one app own a whole prefix of routes, not just one path
  • Django tries patterns top to bottom and calls the view for the first match

Path converters available to `path()`

Path converters available to `path()`
ConverterMatchesType passed to the view
strany non-empty string, no "/"str (the default if omitted)
intdigits onlyint
slugletters, numbers, hyphens, underscoresstr
uuida formatted UUIDuuid.UUID
pathany string, "/" includedstr
How a URLconf routes a request

/articles/42/

ROOT_URLCONF

top-level urlpatterns

first match wins

checked top to bottom

view(request, 42)

  • /articles/42/
    • leads to ROOT_URLCONF
  • ROOT_URLCONF — top-level urlpatterns
    • leads to first match wins
  • first match wins — checked top to bottom
    • leads to view(request, 42)
  • view(request, 42)

Command

python
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("articles/", include("articles.urls")),
]

ROOT_URLCONF points at a module with urlpatterns; path() maps one pattern to a view, include() delegates a prefix, and the first match wins.

4.Views

A view is a Python function (or class) that takes an HttpRequest and returns an HttpResponse. It holds whatever logic is needed to answer the request — reading from a Model, choosing a Template — and it is what a URL pattern points at.

Key features

  • A view is any callable that takes a request and returns an HttpResponse (or subclass)
  • Function-based views (FBVs) are plain functions — direct and explicit
  • Class-based views (CBVs) package common patterns (list, detail, form) as reusable classes
  • A view can also be `async def` for use under ASGI
  • Raising Http404 inside a view is how Django returns a proper 404 response

Function-based vs. class-based views

Function-based vs. class-based views
PropertyFunction-based (FBV)Class-based (CBV)
Shapedef view(request): ...class View(View): def get(self, request): ...
Best forone-off, simple logiccommon patterns — list, detail, create/update
Reusevia plain function compositionvia subclassing and mixins
Registered in urls.py asviews.detailDetailView.as_view()
What every view must satisfy

HttpRequest

always the first argument

view logic

query, decide, render

HttpResponse

always returned

  1. HttpRequest — always the first argument
  2. view logic — query, decide, render
  3. HttpResponse — always returned

Command

python
from django.http import Http404
from django.shortcuts import render
from .models import Article

def detail(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        raise Http404("Article does not exist")
    return render(request, "articles/detail.html", {"article": article})

A view takes an HttpRequest and must return an HttpResponse on every code path — raise Http404 for "not found" rather than returning early with nothing.

5.Models

A model is a Python class subclassing django.db.models.Model — each attribute is a database field, each instance is one row. Django generates a query API (.objects.filter(), .save()) and, via migrations, the real table.

Key features

  • class Name(models.Model): — every model subclasses Model
  • Each class attribute (CharField, DateTimeField, ForeignKey, ...) becomes one column
  • Django adds an `id` primary key automatically unless one is defined
  • `makemigrations` then `migrate` turns a model change into a real schema change
  • Model.objects is the manager — entry point for .filter(), .get(), .create()

Common model field types

Common model field types
FieldStores
CharField(max_length=N)a short string, bounded length required
TextField()an unbounded block of text
IntegerField() / BooleanField()a whole number / True or False
DateTimeField(auto_now_add=True)a timestamp, set once at creation
ForeignKey(Other, on_delete=...)a many-to-one link to another model
From class to table to row

class BlogPost

defines fields

makemigrations + migrate

generates schema

blog_blogpost table

one column per field

BlogPost.objects.create(...)

one row

  1. class BlogPost — defines fields
  2. makemigrations + migrate — generates schema
  3. blog_blogpost table — one column per field
  4. BlogPost.objects.create(...) — one row

Command

python
from django.db import models

class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

A model is a class (fields = columns); Model.objects is how you create/query rows; makemigrations + migrate is what actually changes the database schema.

6.Settings

A Django settings file is a plain Python module — settings.py — with variables like DEBUG and ALLOWED_HOSTS. DJANGO_SETTINGS_MODULE picks which module loads; code reads it via `django.conf.settings`.

Key features

  • A settings module is plain Python — variables, imports, even a for-loop, all legal
  • DJANGO_SETTINGS_MODULE (e.g. "mysite.settings") tells Django which module to load
  • Read settings via `from django.conf import settings`, never the module directly
  • Django loads its own defaults first (global_settings.py), then overrides with yours
  • DEBUG, ALLOWED_HOSTS, and SECRET_KEY are the settings that matter most before deploying

Settings a new project should look at first

Settings a new project should look at first
SettingWhat it controls
DEBUGverbose error pages when True — must be False in production
ALLOWED_HOSTSwhich Host headers Django will accept requests for
SECRET_KEYsigns sessions and CSRF tokens — must stay secret
DATABASESthe database engine, name, host, and credentials
INSTALLED_APPSwhich apps (built-in and yours) the registry loads

Command

bash
export DJANGO_SETTINGS_MODULE=mysite.settings
python manage.py runserver
# or, one-off:
python manage.py runserver --settings=mysite.settings_dev

A settings file is plain Python; DJANGO_SETTINGS_MODULE selects which loads; always read values via `from django.conf import settings`.

7.Templates

The Django Template Language (DTL) renders HTML from a template file plus a context dict: `{{ variable }}` outputs a value, `{% tag %}` provides logic like loops and conditionals. render() (or render_to_string()) combines the two.

Key features

  • {{ variable }} outputs a context value; supports dot lookup like {{ story.title }}
  • {% if %}/{% endif %} and {% for %}/{% endfor %} are the core logic tags
  • render(request, template_name, context) is the usual way a view produces the response
  • The DTL is deliberately limited — no arbitrary Python expressions, by design
  • {% load %} brings in tags/filters from an app, e.g. {% load static %}

Core template syntax

Core template syntax
SyntaxPurpose
{{ value }}output a context variable
{{ value|filter }}transform a value, e.g. {{ name|upper }}
{% if cond %}...{% endif %}conditional rendering
{% for x in list %}...{% endfor %}loop over an iterable
{% load static %}load a tag library before using its tags

Command

python
from django.shortcuts import render

def story_detail(request, pk):
    story = Story.objects.get(pk=pk)
    return render(request, "news/story_detail.html", {"story": story})

{{ }} outputs a value, {% %} is a tag for logic; a template only formats what the view already put in context — it cannot query or run arbitrary Python.

8.Forms

A Form class describes an HTML form the same way a model describes a database table — fields as class attributes. Instantiate it with POST data, call is_valid(), then read validated, type-converted values from cleaned_data.

Key features

  • forms.CharField, forms.EmailField, etc. — one class attribute per form field
  • Unbound: Form() (no data) — for a blank/initial display
  • Bound: Form(request.POST) — carries data to validate
  • is_valid() runs every field's validation and returns True only if all pass
  • cleaned_data holds the validated, type-converted result — read only after is_valid()

Bound vs. unbound forms

Bound vs. unbound forms
PropertyUnboundBound
Created asContactForm()ContactForm(request.POST)
Typical useGET — show a blank formPOST — validate submitted data
is_valid()always FalseTrue only if every field passes
cleaned_datanot availableavailable once is_valid() returns True

Command

python
from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

A Form describes fields like a model describes columns; call is_valid() first, then read values from cleaned_data, never straight from request.POST.

9.Middleware

Middleware is an ordered chain of hooks running on every request/response — MIDDLEWARE lists them top-to-bottom going in, reverse coming out. Each layer can inspect, modify, or short-circuit.

Key features

  • MIDDLEWARE (a setting) is an ordered list — order changes behavior, not just style
  • Each middleware wraps get_response: code before get_response(request) runs on the way in
  • Code after get_response(request) runs on the way back out, in reverse order
  • Returning a response without calling get_response() short-circuits every inner layer
  • AuthenticationMiddleware must come after SessionMiddleware — it reads the session

Default MIDDLEWARE, in the order it runs

Default MIDDLEWARE, in the order it runs
MiddlewareWhat it adds
SecurityMiddlewareHTTPS redirects and security headers
SessionMiddlewarerequest.session
CommonMiddlewarebaseline URL handling
CsrfViewMiddlewareCSRF protection on unsafe methods
AuthenticationMiddlewarerequest.user — requires SessionMiddleware first
MessageMiddlewarethe one-time flash-message store
The onion — request in, response out

SecurityMiddleware

outermost

SessionMiddleware

adds request.session

AuthenticationMiddleware

adds request.user

the view

innermost

  1. SecurityMiddleware — outermost
  2. SessionMiddleware — adds request.session
  3. AuthenticationMiddleware — adds request.user
  4. the view — innermost

Command

python
def simple_middleware(get_response):
    # runs once, at server startup
    def middleware(request):
        # before the view / next layer
        response = get_response(request)
        # after the view / next layer
        return response
    return middleware

MIDDLEWARE order matters: request-phase runs top-to-bottom, response-phase runs bottom-to-top — each layer only sees what earlier ones already added.

10.Admin

The Django admin is an automatic, model-driven interface for managing data — register a model with admin.site.register() and you get a working create/read/update/delete UI at /admin/ with no view or template code of your own.

Key features

  • `admin.site.register(Model)` is the minimum needed for a working CRUD page
  • A ModelAdmin subclass customizes list_display, list_filter, search_fields, and more
  • Requires `python manage.py createsuperuser` to create the first login
  • The docs describe it as an internal management tool, not the site's public front end
  • `@admin.register(Model)` is a decorator shortcut for the same registration

Common ModelAdmin options

Common ModelAdmin options
OptionEffect
list_displaywhich fields show as columns on the change list
list_filteradds a sidebar filter for the named fields
search_fieldsenables a search box over the named fields
readonly_fieldsshows a field's value without allowing edits
prepopulated_fieldsauto-fills one field (e.g. a slug) from another as you type

Command

python
from django.contrib import admin
from .models import Article

admin.site.register(Article)

admin.site.register(Model) generates a CRUD interface from a model's fields — ModelAdmin customizes list_display, list_filter, search_fields.

11.Authentication

Django's auth system provides a User model, authenticate() to check credentials, login()/logout() to attach/clear a session, and request.user on every request. @login_required guards a view.

Key features

  • authenticate(request, username=, password=) verifies credentials, returns User or None
  • login(request, user) attaches the user to the session — separate from authenticate()
  • logout(request) clears all session data for the current request
  • request.user is available on every request — an AnonymousUser instance if not logged in
  • @login_required redirects unauthenticated visitors to LOGIN_URL

The core auth functions

The core auth functions
CallDoes
authenticate(request, username=, password=)checks credentials, returns User or None
login(request, user)attaches user to request.session
logout(request)clears the session
request.user.is_authenticatedTrue for a real user, False for AnonymousUser

Command

python
from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, "dashboard.html", {"user": request.user})

authenticate() checks credentials, returns User or None; login(request, user) is the separate step that starts the session — always check for None first.

12.Sessions

Django's session framework stores per-visitor data server-side, identified via a session ID cookie. SessionMiddleware makes request.session behave like a dict; SESSION_ENGINE picks where data lives.

Key features

  • SessionMiddleware must be in MIDDLEWARE for request.session to exist
  • request.session behaves like a dict: request.session["key"] = value
  • The cookie holds only a session ID — the data itself is stored server-side
  • SESSION_ENGINE picks the backend: db (default), cache, cached_db, file, signed_cookies
  • request.session.flush() clears the session and deletes the cookie entirely

SESSION_ENGINE backends

SESSION_ENGINE backends
BackendStores data in
django.contrib.sessions.backends.dbthe django_session database table (default)
django.contrib.sessions.backends.cachethe configured cache only — fastest, not durable
django.contrib.sessions.backends.cached_dbcache first, database as a write-through backup
django.contrib.sessions.backends.signed_cookiesthe cookie itself, cryptographically signed

Command

python
def post_comment(request, new_comment):
    if request.session.get("has_commented", False):
        return HttpResponse("You've already commented.")
    request.session["has_commented"] = True
    return HttpResponse("Thanks!")

request.session behaves like a dict; the cookie holds only a session ID, not the data — SESSION_ENGINE decides where it lives.

Remember

  • Model = data and rules, Template = presentation only, View = the glue — Django's View plays MVC's Controller role.
  • A view must return an HttpResponse on every code path — raise Http404 for "not found" rather than returning nothing.
  • settings are read via `from django.conf import settings`, never by importing the settings module directly.
  • MIDDLEWARE order matters: request-phase code runs top-to-bottom, response-phase runs bottom-to-top.

Pro tip

  • authenticate() only checks credentials and can return None — always guard before calling login(request, user).
  • A Form's cleaned_data holds validated, converted values; request.POST holds only raw, untrusted strings.
  • Connect signal receivers from AppConfig.ready(), not at plain module import time, to avoid AppRegistryNotReady.
  • Use timezone.now(), never datetime.datetime.now(), once USE_TZ = True — the latter returns an unsafe naive value.

Next up

  • Django Project Structure
  • Request/response lifecycle in depth
  • Models + ORM + PostgreSQL

Sources

  • Django overview — docs.djangoproject.com/en/5.2/intro/overview/
  • Design philosophies — docs.djangoproject.com/en/5.2/misc/design-philosophies/
  • Every claim checked against Django 5.2 LTS docs · checked 2026-08-20

Spotted a mistake on this page? Tell us.