Django cheat sheet

A scannable Django reference: 26 short snippets across 12 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
Project layout and the ORMHow a Django project is structured, how models become database tables, and how to query them without generating an N+1lesson
Views and URLsURL routing with converters and namespaces, function and class-based views, and returning JSON from the same projectlesson
The admin and formsTurn the Django admin into a usable back office, and build server-validated forms with ModelForm — including the CSRFlesson
Templates and the Django template languageA view decides what data the response contains; a template decides how it is written out. The template language islesson
Migrations in depthThese two commands do completely different jobs, and most migration confusion comes from mixing them up. makemigrationslesson
Authentication, users and permissionsUse the built-in auth app for login, sessions and permissions, extend the user model correctly, and understand whatlesson
Static and media filesServe CSS, JavaScript and images correctly in development and production, and accept user uploads without turning yourlesson
Testing Django appsThe client is a request simulator: no network, no server, full access to the response object. Use force_login() insteadlesson
Django REST Framework: serializers and viewsetsA serializer has two jobs: converting model instances to native Python for the response, and validating incoming datalesson
Middleware, signals and management commandsMiddleware is a chain of callables wrapped around the view. The request passes down the list in order; the responselesson
Settings, caching and query performanceSplit settings per environment, cache the expensive parts with a shared backend, and fix the database queries that makelesson
Deployment: gunicorn, static files and the security checklistRun migrations once, from the release job, never from the application's start-up hook. Ten workers booting togetherlesson

Quick snippets

Project layout and the ORM

Layout and settings

pip install "django~=5.1"
django-admin startproject config .      # the trailing dot avoids a nested folder
python manage.py startapp blog

python manage.py migrate                # built-in tables (auth, sessions, admin)
python manage.py runserver              # http://127.0.0.1:8000

Models

python manage.py makemigrations blog   # writes a migration file, review it
python manage.py migrate              # applies it
python manage.py shell

Full lesson: Project layout and the ORM →

Views and URLs

URL configuration

# blog/urls.py
from django.urls import path
from . import views

app_name = "blog"          # namespace, used as blog:detail

urlpatterns = [
    path("", views.post_list, name="list"),
    path("<int:pk>/", views.post_detail, name="detail"),
    path("<slug:slug>/", views.post_by_slug, name="by-slug"),
    path("category/<slug:slug>/", views.category, name="category"),
]

URL configuration

# config/urls.py
from django.contrib import admin
from django.urls import include, path

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

Function and class-based views

# blog/models.py
from django.db import models
from django.urls import reverse

class Post(models.Model):
    ...
    def get_absolute_url(self):
        return reverse("blog:detail", kwargs={"pk": self.pk})

Full lesson: Views and URLs →

The admin and forms

Configuring the admin

python manage.py createsuperuser
# then sign in at /admin/ — only users with is_staff=True may enter

Rendering the form

<form method="post" action="{% url 'blog:comment' post.pk %}">
  {% csrf_token %}
  {{ form.non_field_errors }}
  <div class="field">
    {{ form.text.label_tag }}
    {{ form.text }}
    {{ form.text.errors }}
  </div>
  <button type="submit">Post comment</button>
</form>

Full lesson: The admin and forms →

Templates and the Django template language

Templates and where Django finds them

# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, slug):
    post = get_object_or_404(Post.objects.select_related("author"), slug=slug)
    return render(request, "blog/post_detail.html", {
        "post": post,
        "related": post.category.posts.exclude(pk=post.pk)[:5] if post.category else [],
    })

Filters, custom tags and context processors

{% load blog_extras %}
{{ post.body|reading_time }} min read
<span class="nav-link {% active_class 'list' %}">Posts</span>
{% post_card post show_excerpt=False %}          {# keyword arguments #}
{{ post.title|truncatechars:40 }} {{ post.published_at|date:"j M Y" }}

Full lesson: Templates and the Django template language →

Migrations in depth

makemigrations versus migrate

python manage.py makemigrations blog            # write 0004_post_views.py
python manage.py makemigrations blog --dry-run --verbosity 3   # show the ops first
python manage.py sqlmigrate blog 0004          # print the SQL it will run
python manage.py showmigrations                # [X] = applied, [ ] = pending
python manage.py migrate                       # apply everything pending
python manage.py migrate blog 0003             # migrate back to 0003
python manage.py migrate blog zero             # unapply the whole app

Data migrations

python manage.py makemigrations blog --empty --name backfill_slugs

Changing a live schema safely

python manage.py squashmigrations blog 0001 0009   # after --squashmigrations review
python manage.py migrate --plan                       # what would run, in order
python manage.py migrate blog 0007 --fake             # mark applied without running
python manage.py migrate blog 0007 --fake-initial     # adopt a pre-existing table

Full lesson: Migrations in depth →

Authentication, users and permissions

Login, logout and the user object

# config/urls.py
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("accounts/", include("django.contrib.auth.urls")),   # login, logout, password reset
    path("blog/", include("blog.urls")),
]

Permissions and groups

python manage.py shell -c "
from django.contrib.auth.models import Group, Permission
editors, _ = Group.objects.get_or_create(name='editors')
editors.permissions.add(Permission.objects.get(codename='publish_post'))
"

Extending the user model and password policy

# config/settings.py — what the validators actually enforce
AUTH_PASSWORD_VALIDATORS = [
    {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
    {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
     "OPTIONS": {"min_length": 10}},
    {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
    {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]

# setting or changing a password in code
user.set_password("correct horse battery staple")   # hashes; does not save
user.save(update_fields=["password"])

Full lesson: Authentication, users and permissions →

Static and media files

Static files

# config/settings.py
STATIC_URL = "static/"                       # URL prefix, not a directory
STATICFILES_DIRS = [BASE_DIR / "static"]     # extra source folders you edit
STATIC_ROOT = BASE_DIR / "staticfiles"       # collectstatic output, never edited

STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"},
}

Static files

{% load static %}
<link rel="stylesheet" href="{% static 'css/app.css' %}">
<img src="{% static 'img/logo.svg' %}" alt="Logo" width="120" height="32">
{# with ManifestStaticFilesStorage this becomes /static/css/app.9a1c2f3d.css #}

Serving files in each environment

# config/urls.py — development only
from django.conf import settings
from django.conf.urls.static import static

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

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Full lesson: Static and media files →

Testing Django apps

Forms, fixtures and coverage

python manage.py test --parallel --keepdb        # faster local runs
coverage run manage.py test && coverage report --skip-covered
pytest                                            # with pytest-django + DJANGO_SETTINGS_MODULE

Full lesson: Testing Django apps →

Django REST Framework: serializers and viewsets

Serializers

serializer = PostSerializer(data=request.data)
serializer.is_valid(raise_exception=True)     # -> 400 with a field->messages dict
post = serializer.save(author=request.user)   # extra kwargs go to create()

PostSerializer(post).data                     # serialize one instance
PostSerializer(qs, many=True).data            # serialize a queryset
PostSerializer(post, data=request.data, partial=True).is_valid()   # PATCH

Permissions, pagination and authentication

curl -s http://127.0.0.1:8000/api/posts/?page=2
curl -s -X POST http://127.0.0.1:8000/api/posts/ \
  -H "Authorization: Token $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Hello", "slug": "hello", "body": "..."}'

Full lesson: Django REST Framework: serializers and viewsets →

Middleware, signals and management commands

Management commands

python manage.py expire_drafts --days 30 --archive
# cron: 17 4 * * * /srv/app/.venv/bin/python /srv/app/manage.py expire_drafts --days 30

Full lesson: Middleware, signals and management commands →

Settings, caching and query performance

One base, small deltas per environment

# config/settings/base.py          # everything shared
from pathlib import Path
import environ

BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env(DJANGO_DEBUG=(bool, False), ALLOWED_HOSTS=(list, ["localhost"]))
environ.Env.read_env(BASE_DIR / ".env")      # local convenience, git-ignored

INSTALLED_APPS = [...]

One base, small deltas per environment

# config/settings/production.py
from .base import *        # noqa: F403

DEBUG = False
SECRET_KEY = env("DJANGO_SECRET_KEY")        # no default: fail loudly if unset
ALLOWED_HOSTS = env("ALLOWED_HOSTS")
DATABASES = {"default": env.db("DATABASE_URL")}
CACHES = {"default": env.cache("REDIS_URL")}
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

One base, small deltas per environment

export DJANGO_SETTINGS_MODULE=config.settings.production
python manage.py check --deploy
python manage.py diffsettings          # show what differs from the defaults

Full lesson: Settings, caching and query performance →

Deployment: gunicorn, static files and the security checklist

The release sequence

set -e
python manage.py migrate --noinput          # 1. schema first, backwards compatible
python manage.py collectstatic --noinput     # 2. assets into STATIC_ROOT
python manage.py check --deploy              # 3. fail the release on a red check
# 4. restart the application processes
systemctl restart app-gunicorn
# 5. smoke test the health endpoint through the proxy
curl -fsS https://example.com/healthz

Full lesson: Deployment: gunicorn, static files and the security checklist →

FAQ

Is this Django cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 12 lessons of the Django course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full Django course — it carries the worked explanations, the edge cases and the exercises behind every line here.

Python 3 NumPy pandas Matplotlib Jupyter Notebook Flask

Last refreshed 2026-09-27.