Modern Django project setup for 2026.
Bootstrap
uv init myapp
cd myapp
uv add django djangorestframework django-environ psycopg[binary] gunicorn whitenoise \
celery redis django-redis sentry-sdk structlog python-json-logger drf-spectacular \
djangorestframework-simplejwt django-cors-headers
uv add --dev pytest pytest-django factory-boy ruff mypy django-stubs django-debug-toolbar
Layout
myapp/
├── pyproject.toml
├── manage.py
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── config/
│ ├── settings/
│ │ ├── base.py
│ │ ├── dev.py
│ │ └── prod.py
│ ├── urls.py
│ ├── wsgi.py
│ ├── asgi.py
│ └── celery.py
├── apps/
│ ├── accounts/
│ ├── blog/
│ └── api/
└── tests/
settings/base.py
import environ
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env()
environ.Env.read_env(BASE_DIR / ".env")
SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"rest_framework_simplejwt",
"corsheaders",
"drf_spectacular",
"apps.accounts",
"apps.blog",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
DATABASES = {"default": env.db()}
CACHES = {"default": env.cache()}
AUTH_USER_MODEL = "accounts.User"
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_THROTTLE_RATES": {"anon": "100/hour", "user": "1000/hour"},
}
CELERY_BROKER_URL = env("REDIS_URL")
CELERY_RESULT_BACKEND = env("REDIS_URL")
CELERY_TASK_ACKS_LATE = True
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
settings/dev.py
from .base import *
DEBUG = True
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
INTERNAL_IPS = ["127.0.0.1"]
CORS_ALLOW_ALL_ORIGINS = True
settings/prod.py
from .base import *
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
DEBUG = False
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
sentry_sdk.init(
dsn=env("SENTRY_DSN"),
environment="production",
traces_sample_rate=0.1,
integrations=[DjangoIntegration()],
)
.env.example
DJANGO_SETTINGS_MODULE=config.settings.dev
SECRET_KEY=change-me
DEBUG=1
DATABASE_URL=postgres://postgres:postgres@db:5432/myapp
REDIS_URL=redis://redis:6379/0
ALLOWED_HOSTS=*
SENTRY_DSN=
Dockerfile
FROM python:3.13-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
FROM python:3.13-slim
WORKDIR /app
RUN apt-get update && apt-get install -y libpq5 curl && rm -rf /var/lib/apt/lists/*
RUN useradd -u 1000 -m app
COPY --from=builder --chown=app:app /app/.venv ./.venv
COPY --chown=app:app . .
ENV PATH=/app/.venv/bin:$PATH PYTHONUNBUFFERED=1
USER app
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD curl -fsS http://localhost:8000/health/ || exit 1
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]
docker-compose.yml
services:
web:
build: .
ports: ["8000:8000"]
env_file: .env
depends_on: [db, redis]
volumes: [".:/app"]
worker:
build: .
command: celery -A config worker -l info
env_file: .env
depends_on: [db, redis]
beat:
build: .
command: celery -A config beat -l info
env_file: .env
depends_on: [db, redis]
db:
image: postgres:16
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: postgres
volumes: ["postgres_data:/var/lib/postgresql/data"]
redis:
image: redis:7-alpine
volumes:
postgres_data:
pyproject.toml essentials
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.13"
[tool.ruff]
target-version = "py313"
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "B", "DJ"]
[tool.mypy]
python_version = "3.13"
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
django_settings_module = "config.settings.dev"
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.dev"
addopts = "-ra --reuse-db"
.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: postgres }
ports: ["5432:5432"]
options: --health-cmd pg_isready
redis:
image: redis:7
ports: ["6379:6379"]
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
REDIS_URL: redis://localhost:6379/0
SECRET_KEY: test
DJANGO_SETTINGS_MODULE: config.settings.dev
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv sync
- run: uv run ruff check .
- run: uv run mypy .
- run: uv run python manage.py migrate
- run: uv run pytest --cov
scripts (Makefile)
.PHONY: dev test lint migrate
dev:
docker compose up
test:
uv run pytest
lint:
uv run ruff check . && uv run mypy .
migrate:
uv run python manage.py migrate
Conventions
- One app per bounded domain.
- Custom User from day 1.
- DRF + JWT for API.
- Celery for background work.
- Settings split: base/dev/prod.
- Tests in pytest.
- All migrations committed.
python manage.py check --deploypasses in CI for prod.
Read this next
That’s 20 Django cheatsheets. Next category: Docker.
If you want my full Django starter (uv + DRF + Celery + Docker + CI), it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .