# Django

Data-heavy products with an ORM, forms, accounts and admin.

Django · PostgreSQL · uv · pytest

[Complete guide (Markdown)](/stacks/python-django/guide.md) · [Structured record (JSON)](/stacks/python-django/record.json)

Original source review: 2026-10-10. Core guidance rechecked: 2026-10-13. [Check scope](/research-freshness.json). 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.

- [https://docs.astral.sh/uv/guides/projects/](https://docs.astral.sh/uv/guides/projects/)

### Lint and format: Ruff

Fast lint and formatting feedback with one configuration.

Ruff does not replace static typing, tests, authorization checks or runtime validation.

- [https://docs.astral.sh/ruff/installation/](https://docs.astral.sh/ruff/installation/)

### 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.

- [https://github.com/typeddjango/django-stubs](https://github.com/typeddjango/django-stubs)

- [https://mypy.readthedocs.io/en/stable/getting_started.html](https://mypy.readthedocs.io/en/stable/getting_started.html)

### 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.

- [https://docs.pytest.org/en/stable/getting-started.html](https://docs.pytest.org/en/stable/getting-started.html)

- [https://pytest-django.readthedocs.io/en/latest/](https://pytest-django.readthedocs.io/en/latest/)

### 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.

- [https://docs.djangoproject.com/en/6.0/topics/migrations/](https://docs.djangoproject.com/en/6.0/topics/migrations/)

### 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.

- [https://docs.djangoproject.com/en/6.0/ref/forms/validation/](https://docs.djangoproject.com/en/6.0/ref/forms/validation/)

- [https://pydantic.dev/docs/validation/latest/concepts/strict_mode/](https://pydantic.dev/docs/validation/latest/concepts/strict_mode/)

### 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.

- [https://docs.djangoproject.com/en/6.0/topics/auth/](https://docs.djangoproject.com/en/6.0/topics/auth/)

- [https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/](https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/)

### 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.

- [https://docs.celeryq.dev/en/stable/django/first-steps-with-django.html](https://docs.celeryq.dev/en/stable/django/first-steps-with-django.html)

- [https://docs.celeryq.dev/en/stable/userguide/tasks.html](https://docs.celeryq.dev/en/stable/userguide/tasks.html)

- [https://docs.djangoproject.com/en/6.0/topics/tasks/](https://docs.djangoproject.com/en/6.0/topics/tasks/)

### 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.

- [https://docs.temporal.io/develop/python](https://docs.temporal.io/develop/python)

- [https://docs.celeryq.dev/en/stable/userguide/tasks.html](https://docs.celeryq.dev/en/stable/userguide/tasks.html)

### 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.

- [https://docs.djangoproject.com/en/6.0/howto/deployment/](https://docs.djangoproject.com/en/6.0/howto/deployment/)

- [https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/](https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/)

- [https://docs.astral.sh/uv/guides/integration/docker/](https://docs.astral.sh/uv/guides/integration/docker/)

### 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.

- [https://docs.python.org/3/library/logging.html](https://docs.python.org/3/library/logging.html)

### 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.

- [https://tenacity.readthedocs.io/en/latest/](https://tenacity.readthedocs.io/en/latest/)

### 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.

- [https://docs.djangoproject.com/en/6.0/topics/email/](https://docs.djangoproject.com/en/6.0/topics/email/)

### 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.

- [https://docs.djangoproject.com/en/6.0/topics/files/](https://docs.djangoproject.com/en/6.0/topics/files/)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/intro/tutorial01/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/migrations/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/db/transactions/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/tasks/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/http/sessions/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/auth/default/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/cache/)
[channels.readthedocs.io →](https://channels.readthedocs.io/en/stable/topics/channel_layers.html)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/userguide/tasks.html)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/getting-started/first-steps-with-celery.html)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/migrations/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/db/transactions/)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/tasks/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/db/transactions/)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/userguide/tasks.html)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/http/sessions/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/auth/default/)

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.

[channels.readthedocs.io →](https://channels.readthedocs.io/en/stable/topics/channel_layers.html)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/cache/)
[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/topics/db/transactions/)

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.

[docs.djangoproject.com →](https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/)
[docs.celeryq.dev →](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html)

[Read the migration workflow →](/rewrite/guide.md)

## 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](https://docs.astral.sh/uv/guides/projects/)

- [Ruff installation](https://docs.astral.sh/ruff/installation/)

- [django-stubs](https://github.com/typeddjango/django-stubs)

- [mypy getting started](https://mypy.readthedocs.io/en/stable/getting_started.html)

- [pytest getting started](https://docs.pytest.org/en/stable/getting-started.html)

- [pytest-django](https://pytest-django.readthedocs.io/en/latest/)

- [Django migrations](https://docs.djangoproject.com/en/6.0/topics/migrations/)

- [Django form validation](https://docs.djangoproject.com/en/6.0/ref/forms/validation/)

- [Pydantic strict mode](https://pydantic.dev/docs/validation/latest/concepts/strict_mode/)

- [Django authentication](https://docs.djangoproject.com/en/6.0/topics/auth/)

- [Django deployment checklist](https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/)

- [Celery Django integration](https://docs.celeryq.dev/en/stable/django/first-steps-with-django.html)

- [Celery tasks](https://docs.celeryq.dev/en/stable/userguide/tasks.html)

- [Django Tasks](https://docs.djangoproject.com/en/6.0/topics/tasks/)

- [Temporal Python SDK](https://docs.temporal.io/develop/python)

- [Django deployment](https://docs.djangoproject.com/en/6.0/howto/deployment/)

- [uv Docker](https://docs.astral.sh/uv/guides/integration/docker/)

- [Python logging](https://docs.python.org/3/library/logging.html)

- [Tenacity documentation](https://tenacity.readthedocs.io/en/latest/)

- [Django sending email](https://docs.djangoproject.com/en/6.0/topics/email/)

- [Django files](https://docs.djangoproject.com/en/6.0/topics/files/)

---

Canonical page: https://stack.menu/stacks/python-django/
