Django
Data-heavy products with an ORM, forms, accounts and admin.
Django · PostgreSQL · uv · pytest
Complete guide (Markdown) · Structured record (JSON)
Original source review: 2026-10-10. Core guidance rechecked: 2026-10-13. Check scope. Dependency combinations and setup need project-specific qualification.
Use cases
- CRUD-heavy SaaS
- Internal products and admin tools
- Server-rendered business applications
Choose it when
- Accounts, forms, admin and database workflows are central
- A cohesive conventional monolith reduces integration decisions
Consider another path when
- Only a small stateless endpoint is needed
- The core workload needs a different runtime or hard real-time guarantees
Working practices
- Keep Django's auth, session and CSRF protections. Enforce tenant and object permissions in application operations.
- Use forms, domain validation and database constraints together; do not rely on one validation layer.
- Commit and rehearse migrations against an isolated database; check deployment settings separately from unit tests.
- Choose WSGI or ASGI for the actual workload and operate background workers independently.
Tools by job
Packaging and environments: uv + pyproject.toml + committed uv.lock
One project entry point for dependencies, Python selection, execution and reproducible sync.
Pin a compatible Python minor and resolve actual package versions at implementation; do not hand-edit uv.lock. Keep environments and caches in a local development workspace.
Lint and format: Ruff
Fast lint and formatting feedback with one configuration.
Ruff does not replace static typing, tests, authorization checks or runtime validation.
Static typing: mypy + django-stubs plugin
Django-aware stubs and plugin improve checks around ORM and settings.
Check the django-stubs compatibility table for the selected Django/Python/mypy combination; plugin imports application settings.
Testing: pytest + pytest-django
Database fixtures and Django integration while retaining pytest conventions.
Set DJANGO_SETTINGS_MODULE; use isolated PostgreSQL tests for constraints and transactions. Unit tests alone miss request and migration behavior.
Database and migrations: PostgreSQL + Django ORM/migrations + Psycopg
Use the framework's existing data layer and schema history.
Review schema/data migrations and query counts; run production-relevant DB tests. Do not introduce SQLAlchemy as a second default ORM.
Validation: Django Forms/ModelForms plus domain checks and DB constraints
Form lifecycle handles submitted values and field errors.
Model saving is not a substitute for form/domain validation; enforce invariants at the appropriate transaction boundary.
Authentication: Django auth, sessions, permissions and CSRF protection
Built-in accounts and session conventions fit browser products.
Object/tenant permissions still need explicit implementation and tests; do not expose the admin as the product UI by default.
Background jobs: Celery + a configured durable broker
Documented Django integration for off-request work.
Broker required; enqueue after commit, retry only safe effects. Django Tasks built-in backends do not supply production workers or durable queues. Celery is a separate integration, not automatically a django.tasks backend.
Long-running durable agents: Temporal Python SDK when recovery across restarts is required
Persisted workflow orchestration and separate worker processes suit retries, pauses and human waits.
Adds a service and operational cost. Keep external I/O in activities, make side effects idempotent, test replay and cancellation; no end-to-end integration was run.
Deployment: Container or managed Python service; WSGI for sync applications, ASGI when justified
Use the Django production deployment interface and checklist.
Choose/pin a compatible application server at implementation; never deploy runserver. Configure secrets, HTTPS, static assets, health checks and backups.
Application logging: Python logging with configured handlers
Shared standard logging captures application and dependency diagnostics
Configure logging at entry point, redact credentials and add request/job IDs, duration and failure context. Persist logs on the execution platform; metrics/tracing backend is a separate operational choice.
Bounded external-call retries: Tenacity only for retryable transient failures
Explicit stop/wait/filter rules support controlled retries
Install tenacity; set attempt/deadline limits, jitter and exception filters. The bare decorator retries forever without delay; do not stack retries with SDK/worker retries or repeat non-idempotent effects without keys.
Transactional email: django.core.mail + selected SMTP backend
Built-in mail wrappers and test backends fit account/product messages
Configure TLS, sender and backend; test HTML/text content and enqueue required delivery. Delivery and bounce monitoring remain provider responsibilities.
Uploads and file storage: Django FileField + configured storage backend
Framework file APIs separate model metadata from storage
Configure storage for the selected host; validate upload size/type and authorize serving. Local MEDIA_ROOT is unsuitable for unshared ephemeral replicas; choose an external backend when required.
Build with the stack’s strengths
Architecture and developer workflow.
Django organizes projects into applications, with conventional URL/view/model boundaries and integrated authentication. Recommendation: keep domain operations and permissions reusable across HTTP views, admin actions and workers. Optional Celery and Channels are separate execution/messaging systems rather than core Django features.
Version scope: Django 6.0 versioned docs; Celery 5.6.3 and Channels 4.3.2 current docs fetched 11 October 2026. The Django 6 Tasks API is version-specific and does not imply an existing Django 5.2 project should upgrade or abandon Celery. Pin actual Python/package versions before qualifying composition. · Reviewed 2026-10-11
Architecture boundaries
- Django defaults to database autocommit; transaction.atomic defines an explicit transaction boundary. Recommendation: keep transactions around the domain operation and do external delivery after commit through a recoverable design when required.
- Django 6 Tasks defines and enqueues tasks but supplies no worker mechanism. A production backend/worker must be evaluated; Celery remains a separate established candidate. Recommendation: do not equate an API-level task declaration with durable execution.
- Channels channel layers exchange high-level application events. channels_redis is the official production-supported layer; the in-memory layer cannot communicate across processes. Recommendation: persist business state separately from transient fanout.
Tools and feedback
- In a disposable target, django-admin startproject mysite creates the project and python manage.py startapp polls creates an application. Preserve the existing project/application structure when migrating a feature.
- Use python manage.py makemigrations to prepare reviewed migration files, python manage.py migrate to apply them and python manage.py showmigrations to inspect recorded state. Recommendation: nominate one deployment migration runner for shared tables.
- For a configured Celery application, adapt celery -A tasks worker --loglevel=INFO from the official first-steps recipe, replacing tasks with the actual application module. Broker and optional result backend are separate configuration choices.
- Before a production target, adapt python manage.py check --deploy to its production settings as the checklist instructs. Recommendation: exercise real database, session, task worker and Channels topology; the development runserver is not a production deployment.
Translations to avoid
- Avoid deploying Django 6 ImmediateBackend/DummyBackend as a substitute for a durable production queue; the Tasks framework requires an external executing backend and documents built-in development/test backends.
- Avoid treating transaction.on_commit as durable cross-store delivery: callbacks run after commit and their failures cannot roll back the transaction. Recommendation: use an outbox when lost dispatch after commit is unacceptable.
- Avoid using LocMemCache or Channels in-memory layers as shared multi-process state; both are process-local. Avoid using cache-only session storage when eviction/restart logout violates the product contract.
- Avoid running two Celery beat schedulers for the same schedule; Celery documents duplicate task production. Recommendation: transfer ownership across frameworks as well as within Celery.
For an existing application
Keep the contracts while you change the stack.
These are documented options and proposed checks. Inspect the actual source application before adopting a transition.
Data and schema
Inspect the source
Capture schema, account IDs, database constraints and model/write side effects. Migration history is framework-owned metadata and must be reconciled deliberately when an existing schema changes owner.
During coexistence
Recommendation: retain one domain writer and schema runner; add compatible fields and move one application/domain boundary while old code still reads new records. Use historical models for Django data migrations as documented.
Target design
Django models/migrations with explicit transaction.atomic boundaries for related database writes. Recommendation: delay field deletion until the rollback reader retires.
Rollback
Recommendation: restore the old writer while retaining additive fields; rehearse reverse migrations on a disposable copy and reconcile non-reversible data operations.
Benefits and prerequisites
Recommendation: coherent model and migration ownership makes a domain boundary reviewable.
Requires: Tested schema mapping and explicit writer transfer.
Tradeoffs: Database reversal may be irreversible for data operations; application objects/cache state also need reconciliation.
Checks before transfer
- Create in each permitted writer and read in both runtimes; compare identifiers, null values, timestamps and constraint failures.
- Fail inside transaction.atomic and verify database writes roll back; inspect whether in-memory/cache state requires explicit restoration.
Jobs and schedules
Inspect the source
Inventory task names, argument serializer, broker/result backend, retry/acknowledgement policy, periodic schedule and ready/delayed/failed/running work. Django 6 Tasks and Celery payloads are distinct contracts.
During coexistence
Recommendation: keep old consumers for old tasks; isolate new categories and route producers deliberately. Retain the existing scheduler until an explicit one-owner transfer. Qualify any Django Tasks backend separately from the task API.
Target design
Celery with a selected broker/optional result backend, or a qualified Django 6 executing backend. Schedule dispatch after database commit; recommendation: add recoverable outbox delivery when a crash can occur before broker acceptance.
Rollback
Recommendation: pause new producers, retain consumers for accepted payloads and reconcile completed effects before switching categories back.
Benefits and prerequisites
Recommendation: execution can migrate by task category while the old backlog remains executable.
Requires: Qualified payload fixtures, retained consumers and one schedule owner.
Tradeoffs: Two task systems may be operated; a framework API switch does not translate backlog or broker guarantees.
Checks before transfer
- Capture a task envelope and run it through its intended consumer, including argument conversion and missing-record behavior.
- Rollback the domain transaction and verify no dispatch; kill after an external effect and validate replay-safe completion.
- Account for delayed/retry/failed/in-flight tasks and confirm one periodic enqueue for each transferred schedule.
Identity and authorization
Inspect the source
Record authentication backend, user mapping, session engine/cookie name, serializer, expiry and revocation. Django database, cached_db, cache-only and signed-cookie sessions have different persistence/failure behavior.
During coexistence
Recommendation: retain a shared identity authority or qualified exchange; do not treat Redis key sharing as session-format compatibility. Keep every resource permission enforced in the target view/operation.
Target design
Django authentication with a selected session backend. cached_db writes through to database/cache and reads the database on cache misses; recommendation: prefer it over cache-only sessions when eviction logout is unacceptable.
Rollback
Recommendation: preserve previous identity/session contracts through the rollback window and retain revocation state during traffic reversal.
Benefits and prerequisites
Recommendation: a durable session authority can preserve access through cache eviction.
Requires: Configured database sessions, compatible account mapping and tested revocation.
Tradeoffs: Write-through caching adds operations; cookie continuity across runtimes still requires a decoder/exchange or planned reauthentication.
Checks before transfer
- Login in the old system, access the Django feature, then logout/expire/revoke and verify both deny unauthorized operations.
- Evict the selected session cache and confirm behavior matches the chosen engine; test another tenant’s identifier with a valid login.
Realtime transport
Inspect the source
Capture WebSocket protocol, group/event names, authentication and required missed-event behavior. Channels channel layers carry application-level events, not a universal client protocol.
During coexistence
Recommendation: keep old client endpoints, bridge versioned domain events and route room/user cohorts including reconnects. Designate one committed-change publisher; use the configured shared layer for multiple processes.
Target design
Channels with channels_redis when using the documented official production channel layer. Recommendation: reconstruct/replay from persistent domain state when missed broadcasts matter.
Rollback
Recommendation: retain the previous endpoint/event envelope and route cohorts back including their reconnects; avoid replaying already-applied effects.
Benefits and prerequisites
Recommendation: a shared channel layer enables cross-process fanout.
Requires: Configured Redis layer, matching consumers/events and authorized subscriptions.
Tradeoffs: Redis remains operated; transient fanout alone does not provide durable business-message recovery.
Checks before transfer
- Connect subscribers to separate processes and verify one committed change reaches both authorized groups.
- Restart/disconnect during an event and verify reconnect obtains missing domain state; revoke access and confirm rejected subscriptions.
Caching and shared state
Inspect the source
Inventory key versions/prefixes, serializers, TTLs and invalidation, separating disposable values from session/coordination ownership. Django local-memory cache is per-process even though it is thread-safe.
During coexistence
Recommendation: use separate namespaces for changed representations and invalidate both old/new readers. Retain shared storage for cross-process requirements; move only reconstructible local data to LocMemCache.
Target design
A deliberately selected Django cache backend with configured timeout/key version. Recommendation: use local caches only where node/process staleness is accepted and rebuilding is safe.
Rollback
Recommendation: restore old namespace/backend routing and repopulate from authoritative data without treating cache contents as business records.
Benefits and prerequisites
Recommendation: reconstructible caching can change independently of domain writes.
Requires: Defined staleness, invalidation and data ownership.
Tradeoffs: Per-process caches duplicate state; sensitive filesystem cache locations/serialization require care as the docs explain.
Checks before transfer
- Warm two processes, commit a source update and verify both readers meet the stated staleness policy.
- Restart or empty only the dedicated cache and verify reconstruction; session/permission correctness must remain intact.
Delivery and recovery
Inspect the source
List production application server, settings/secrets, migrations, static/media handling, Celery worker/beat and Channels processes. Celery requires only one scheduler for a schedule at a time.
During coexistence
Recommendation: keep both process inventories and one named migration/schedule owner during coexistence. Define promotion thresholds for outcomes, queue age and denied access rather than promoting from a clean startup alone.
Target design
An explicitly configured deployment following Django’s checklist, plus independently supervised selected workers and scheduler. Recommendation: document restored database/session/queue consistency.
Rollback
Recommendation: restore old routing/settings and schedule ownership explicitly; preserve accepted tasks and reconcile effects before replay.
Benefits and prerequisites
Recommendation: production checks and role ownership make recovery responsibilities visible.
Requires: Actual production settings and a rehearsed restore/restart.
Tradeoffs: check --deploy detects configuration issues, not end-to-end job, permission or restore correctness.
Checks before transfer
- Run the deployment checks against intended settings and exercise an authorized/denied request after a disposable restore.
- Stop a worker and verify queued work is visible; restart it, then verify one scheduler and intended recovery behavior.
Setup
- In a new local source directory in a local development workspace: uv init; choose and pin a supported Python minor, then uv add django 'psycopg[binary]'. Resolve compatible versions and commit uv.lock.
- uv add --dev ruff mypy django-stubs pytest pytest-django
- uv run django-admin startproject config . ; then uv run python manage.py startapp core
- Configure installed apps, PostgreSQL credentials from the environment, auth/session settings, pytest DJANGO_SETTINGS_MODULE and the django-stubs mypy plugin/settings module.
- Implement one authorized vertical slice; generate migrations with uv run python manage.py makemigrations, review them, then uv run python manage.py migrate against a disposable development DB.
- Add Celery only for actual queued work; configure broker, worker and after-commit enqueueing. Add Temporal only when durable multi-step recovery is required.
- Document application server, static asset collection, migration release ordering, worker start/restart and rollback. Run verification below before deployment.
Verification
Run these in the configured application; they are instructions, not recorded passes.
uv sync --locked uv run ruff check . uv run ruff format --check . uv run mypy core config uv run pytest uv run python manage.py check uv run python manage.py makemigrations --check --dry-run uv run python manage.py migrate --check uv run python manage.py check --deploy --settings=config.production
Open questions
- Which supported Python/Django versions and third-party packages are required?
- Which host owns HTTPS, static/media serving and deployment settings?
- Which identity, email/storage, broker and job-delivery guarantees are required?
Sources
- uv projects
- Ruff installation
- django-stubs
- mypy getting started
- pytest getting started
- pytest-django
- Django migrations
- Django form validation
- Pydantic strict mode
- Django authentication
- Django deployment checklist
- Celery Django integration
- Celery tasks
- Django Tasks
- Temporal Python SDK
- Django deployment
- uv Docker
- Python logging
- Tenacity documentation
- Django sending email
- Django files