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
| Topic | What it covers | |
|---|---|---|
| Project layout and the ORM | How a Django project is structured, how models become database tables, and how to query them without generating an N+1 | lesson |
| Views and URLs | URL routing with converters and namespaces, function and class-based views, and returning JSON from the same project | lesson |
| The admin and forms | Turn the Django admin into a usable back office, and build server-validated forms with ModelForm — including the CSRF | lesson |
| Templates and the Django template language | A view decides what data the response contains; a template decides how it is written out. The template language is | lesson |
| Migrations in depth | These two commands do completely different jobs, and most migration confusion comes from mixing them up. makemigrations | lesson |
| Authentication, users and permissions | Use the built-in auth app for login, sessions and permissions, extend the user model correctly, and understand what | lesson |
| Static and media files | Serve CSS, JavaScript and images correctly in development and production, and accept user uploads without turning your | lesson |
| Testing Django apps | The client is a request simulator: no network, no server, full access to the response object. Use force_login() instead | lesson |
| Django REST Framework: serializers and viewsets | A serializer has two jobs: converting model instances to native Python for the response, and validating incoming data | lesson |
| Middleware, signals and management commands | Middleware is a chain of callables wrapped around the view. The request passes down the list in order; the response | lesson |
| Settings, caching and query performance | Split settings per environment, cache the expensive parts with a shared backend, and fix the database queries that make | lesson |
| Deployment: gunicorn, static files and the security checklist | Run migrations once, from the release job, never from the application's start-up hook. Ten workers booting together | lesson |
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 shellFull 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})
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 tableFull 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_MODULEFull 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 30Full 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 defaultsFull 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/healthzFull lesson: Deployment: gunicorn, static files and the security checklist →
FAQ
Is this Django cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
Python 3 NumPy pandas Matplotlib Jupyter Notebook Flask
Last refreshed 2026-09-27.