cheat sheet
django
Package-level reference for Django on PyPI — install variants, LTS release cadence, ecosystem companions, and alternatives.
django
What it is
django is a full-stack web framework first released in 2005 and maintained by the Django Software Foundation. It ships with an ORM, migrations, admin panel, authentication, forms, template engine, caching, and middleware — the "batteries included" philosophy taken to its logical end.
Reach for Django when you want a single coherent stack for a database-backed app, when you need an admin UI for free, or when you're building a team-sized codebase that benefits from strong conventions. Reach for Flask/FastAPI when you want a smaller surface area or are building a pure API with a different ORM.
Install
pip install django
Output: (none — exits 0 on success)
uv add django
Output: dependency resolved + added to pyproject.toml
poetry add django
Output: updated lockfile + virtualenv install
pip install "django[argon2]" # argon2 password hashing
pip install "django[bcrypt]" # bcrypt password hashing
Output: Django plus the named optional password-hashing backend
Versioning & Python support
- Predictable feature-release cadence: a new minor release every 8 months, and a new major (
X.0) release roughly every 2 years. Major bumps coincide with deprecation cleanups. - LTS releases are issued every 2 years and receive security fixes for ~3 years total. Recent LTS lines:
4.2LTS (2023),5.2LTS (2025). Non-LTS releases are supported for ~16 months. - Supports Python 3.10+ on the latest stable line; older releases support 3.8/3.9. Each Django version explicitly lists supported Python versions.
- Strict deprecation policy — features are deprecated for at least 2 releases before removal.
Package metadata
- Maintainer: Django Software Foundation
- Project home: github.com/django/django
- Docs: docs.djangoproject.com
- PyPI: pypi.org/project/django
- License: BSD-3-Clause
- Governance: Django Software Foundation (non-profit), elected Technical Board
- First released: 2005
- Downloads: ~50M+/month — the dominant full-stack framework in Python
Optional dependencies & extras
django[argon2]— installsargon2-cffifor Argon2 password hashing (the most secure built-in choice).django[bcrypt]— installsbcryptfor bcrypt password hashing (interop with legacy systems).
Core dependencies pulled in automatically:
asgiref— async/sync bridge forasync defviewssqlparse— SQL formatting for the debug toolbar / shell
Common companion packages typically installed alongside:
psycopg[binary](orpsycopg2-binary) — PostgreSQL adapter; preferred over MySQL for new projectsmysqlclient— MySQL/MariaDB adaptergunicorn/uvicorn/daphne— production WSGI / ASGI serverswhitenoise— static-file serving without nginxdjango-environ/python-dotenv—.envconfigurationdjango-debug-toolbar— request inspector for developmentdjango-extensions—shell_plus,runserver_plus, and many utility commandsdjangorestframework(DRF) — the canonical REST API layerdjango-ninja— typed, FastAPI-style API layer for Djangocelery+redis— background tasksdjango-storages— S3, GCS, Azure blob storage backendspillow— required forImageField
Alternatives
| Package | Trade-off |
|---|---|
flask | Microframework, sync, you pick the ORM. Use for small services or full stack control. |
fastapi | Async, type-driven, auto OpenAPI. Use for new typed REST APIs without an admin UI. |
litestar | Async, strict typing, high throughput. Use when raw API speed matters more than batteries. |
django-ninja | Add-on, not a replacement — typed APIs on top of Django models. Use to keep the admin/ORM but build typed endpoints. |
tornado / aiohttp | Lower-level async servers. Use only when you want to build a framework yourself. |
Common gotchas
- LTS vs non-LTS support windows differ. LTS = ~3 years of security fixes; non-LTS = ~16 months. Production projects on a non-LTS release have a short upgrade clock.
psycopg2-binaryis fine for dev, not production. It bundles a vendored libpq. For production preferpsycopg[binary](the modern v3 driver) orpsycopg2built from source against the system libpq.DEBUG = Trueleaks secrets. The default error page renders settings + traceback. Never deploy withDEBUG = True. TheSECURE_*checklist (python manage.py check --deploy) catches this and several other footguns.ALLOWED_HOSTSmust be set in production. A request with an unrecognizedHostheader returns 400 — necessary security default, but a common first-deploy surprise.- Migrations live in each app. A model change without
python manage.py makemigrationsproduces silently-stale schema. CI should runmakemigrations --checkto catch missed migration files. async defviews need an ASGI server. Runninggunicorn(WSGI) with async views works but each request becomes a sync-to-async bridge — no real concurrency. Useuvicornordaphnefor true async.- The
django-adminvsmanage.pydistinction.django-adminworks withoutDJANGO_SETTINGS_MODULE;manage.pyauto-sets it frommanage.py's parent. Usemanage.pyinside a project,django-admin startprojectonly to bootstrap. - Third-party-app ordering matters. Apps listed earlier in
INSTALLED_APPSwin on template lookup, signal registration, and management-command shadowing. The convention is: Django apps first, third-party apps next, local apps last.
Production deployment
Django supports two deployment modes: WSGI (sync, gunicorn/uWSGI) and ASGI (async, daphne/uvicorn/hypercorn). Choose ASGI when you actually use async def views, websockets, or channels; otherwise WSGI is simpler and well-trodden.
# WSGI — gunicorn (most common production setup)
pip install gunicorn
gunicorn myproject.wsgi:application \
--workers 4 \
--threads 2 \
--worker-class gthread \
--bind 0.0.0.0:8000 \
--timeout 60 \
--max-requests 1000 \
--max-requests-jitter 50 \
--access-logfile -
Output: classic Django prod server; pairs with nginx for TLS termination and static-file serving.
# ASGI — daphne (Django Channels' reference server)
pip install daphne
daphne -b 0.0.0.0 -p 8000 myproject.asgi:application
Output: required if you use channels (websockets); also handles regular HTTP.
# ASGI — uvicorn with workers
pip install "uvicorn[standard]"
uvicorn myproject.asgi:application --host 0.0.0.0 --port 8000 --workers 4 --proxy-headers
Output: fastest ASGI option in benchmarks; preferred for pure async apps.
# settings.py — required prod-only adjustments
import os
DEBUG = False
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_HSTS_SECONDS = 31536000
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
# Static files via WhiteNoise (no nginx required)
MIDDLEWARE = ["django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", ...]
STORAGES = {
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}
Output: production-safe settings; manage.py check --deploy audits these automatically.
# Deploy sequence inside a container/CI
python manage.py collectstatic --noinput
python manage.py migrate --noinput
python manage.py check --deploy
gunicorn myproject.wsgi:application -c gunicorn.conf.py
Output: standard 4-step boot — collect static assets, run migrations, audit security settings, start the server.
Version migration guide
Django's deprecation policy is the strictest in the Python ecosystem — features are deprecated for at least two releases before removal, so the migration path is well-marked. The big jumps in recent memory:
3.2 LTS → 4.0:
- Default
USE_TZ = True(timezone-aware datetimes by default). pytzremoved from core in favor ofzoneinfo— keeppytzinstalled only if you depend on it.django.utils.encoding.force_text()removed — useforce_str().
4.2 LTS → 5.0:
db_defaultfor model fields (DB-side defaults).- Async ORM methods —
Model.objects.aget(),acreate(),afilter(), etc. - Form-field
assume_schemeforURLField. - Python 3.10+ required.
5.0 → 5.2 LTS:
- Composite primary keys via
CompositePrimaryKey. - More async ORM coverage (related-manager methods).
BoundFieldAPI extensions.
Migration playbook for any major bump:
# 1. Pin the new version in a branch, install
pip install --upgrade 'django~=5.2'
# 2. Surface deprecation warnings
python -W error::DeprecationWarning manage.py test
# 3. Run the deploy checklist
python manage.py check --deploy
# 4. Run migrations against a staging DB clone
python manage.py migrate
# 5. Run the full test suite + a smoke test of admin/API endpoints
Output: warnings → errors flag every line that needs attention; do not upgrade past LTS until all are addressed.
Security considerations
Django has the most opinionated security model of any Python framework — most defaults are safe, but a handful of settings have to be turned ON for production. manage.py check --deploy is the canonical audit.
DEBUG = Falsein prod. Non-negotiable. WithDEBUG=True, the error page exposes settings and traceback.SECRET_KEYfrom env; rotate viaSECRET_KEY_FALLBACKS(Django 4.1+) for zero-downtime rotation.ALLOWED_HOSTSmust list every public hostname. Wildcards (*) are dangerous unless behind a strict LB.- CSRF protection is on by default. Don't disable it casually. AJAX must include the
X-CSRFTokenheader (or post the form token). SECURE_*middleware family. EnableSECURE_HSTS_SECONDS,SECURE_HSTS_INCLUDE_SUBDOMAINS,SECURE_HSTS_PRELOAD,SECURE_SSL_REDIRECT,SESSION_COOKIE_SECURE,CSRF_COOKIE_SECURE,SECURE_REFERRER_POLICY.X_FRAME_OPTIONS = "DENY"unless you genuinely embed your app in an iframe.- ORM raw queries.
Model.objects.raw(sql, params)is parameterized;.extra()andconnection.cursor().execute(sql)need careful escaping. Never f-string user input into SQL. - Password hashing. Default is PBKDF2;
django[argon2]is the recommended upgrade.bcryptis acceptable.MD5PasswordHasheris for legacy import only. - Admin URL. Move
/admin/to a non-guessable path AND add IP allow-list / 2FA. Public admin URLs invite credential-stuffing. - File uploads. Validate content-type AND magic bytes; never trust the client filename. Use
django-storagesto push uploads off the app server. - CVE history. Subscribe to
django-announce; LTS releases get same-day patches for high-severity issues.
Real-world recipes
Package-level patterns — settings layout, signal usage, custom managers, ASGI bootstrapping. The companion python/django.md covers model and view basics; the recipes here target the structural choices teams make once an app is past the tutorial stage.
# Recipe 1 — Layered settings module
# settings/__init__.py
import os
env = os.environ.get("DJANGO_ENV", "dev")
if env == "prod": from .prod import *
elif env == "test": from .test import *
else: from .dev import *
Output: DJANGO_ENV picks the right module at startup; each file imports from a base.py for shared keys.
# Recipe 2 — Custom manager with reusable querysets
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(published=True)
class Article(models.Model):
title = models.CharField(max_length=200)
published = models.BooleanField(default=False)
objects = models.Manager()
published_articles = PublishedManager()
Output: Article.published_articles.all() returns only published rows; Article.objects.all() returns everything.
# Recipe 3 — Signal for "after user save" side effects
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
@receiver(post_save, sender=get_user_model())
def create_profile(sender, instance, created, **kw):
if created:
Profile.objects.create(user=instance)
Output: new users automatically get a Profile row; receivers fire after every save (filter with created).
# Recipe 4 — Raw SQL with safe parameter binding
from django.db import connection
def top_authors(limit: int):
with connection.cursor() as cur:
cur.execute("SELECT author_id, COUNT(*) FROM blog_article "
"GROUP BY author_id ORDER BY 2 DESC LIMIT %s", [limit])
return cur.fetchall()
Output: parameterized %s placeholder; never f-string the limit value.
# Recipe 5 — Async view + async ORM (Django 5+)
async def home(request):
articles = [a async for a in Article.published_articles.all()[:10]]
return JsonResponse({"articles": [a.title for a in articles]})
Output: native async iteration over a queryset; requires an ASGI server to actually use a single event loop.
Troubleshooting common errors
| Error / Symptom | Likely cause | Fix |
|---|---|---|
DisallowedHost: Invalid HTTP_HOST header | ALLOWED_HOSTS missing the request host | Add the host to ALLOWED_HOSTS; do not use * in production. |
OperationalError: FATAL: password authentication failed | Wrong DB creds or wrong DATABASES["default"] | Verify DATABASE_URL; use django-environ to avoid quoting bugs. |
django.db.utils.ProgrammingError: relation "..." does not exist | Migrations not applied | python manage.py migrate; CI should run migrate --check for staleness. |
ImproperlyConfigured: SECRET_KEY setting must not be empty | Settings module not loaded or env var missing | Set DJANGO_SETTINGS_MODULE; load .env before django.setup(). |
RuntimeError: SynchronousOnlyOperation | Calling sync ORM from async def | Wrap in sync_to_async or use the new a* async ORM methods. |
CSRF verification failed. Request aborted. | Form missing {% csrf_token %} or AJAX missing header | Add token; for AJAX include X-CSRFToken from the cookie. |
TemplateDoesNotExist | APP_DIRS off or template dir missing | Add app to INSTALLED_APPS; ensure TEMPLATES[0]["APP_DIRS"] = True. |
Migration conflicts detected | Two branches added migrations at the same node | python manage.py makemigrations --merge or rebase one branch's migration. |
| 502/504 from gunicorn under load | --timeout too short or workers blocked on slow query | Raise --timeout; profile slow queries; use gthread worker. |
| Static files 404 in production | collectstatic not run or STATIC_ROOT not served | Run collectstatic; configure WhiteNoise or nginx for /static/. |
See also
- Python: django — API tutorial, models, views, admin, ORM
- Concept: API — REST design fundamentals
- Concept: HTTP — protocol fundamentals
- Packages: pip-flask — the microframework alternative