Django Project Instructions
CLAUDE.md for Django projects: app structure, ORM query rules, migration workflow, layered settings, and pytest-django conventions agents must follow before touching models.
Use when: Use in a Django 5.x codebase so agents handle migrations safely, keep business logic out of views, and avoid N+1 queries when writing or reviewing ORM code.
The skill file
Claude Code · CLAUDE.mdCLAUDE.md3456 chars
# Project Instructions — Django
## Commands
```bash
uv run python manage.py runserver # dev server → http://localhost:8000
uv run pytest # full test suite (pytest-django, not manage.py test)
uv run pytest apps/orders -x # focused run while iterating
uv run python manage.py makemigrations # after any model change
uv run python manage.py migrate # apply locally
uv run python manage.py makemigrations --check --dry-run # CI gate: fails if migrations are missing
uv run ruff format . && uv run ruff check --fix .
```
## App structure
Apps live under `apps/` and are small and domain-focused (`apps/orders`, `apps/accounts`). Standard layout per app:
```
apps/orders/
models.py # schema only: fields, Meta, constraints, simple properties
services.py # business logic — all writes go through functions here
selectors.py # read queries used by views/tasks
views.py / api.py
admin.py
migrations/
tests/
```
- **Views stay thin.** A view parses input, calls one service/selector function, returns a response. If a view grows conditional business logic, move it to `services.py`.
- Cross-app imports go through the other app's `services.py`/`selectors.py`, never its models' managers directly.
## ORM rules
- Every queryset that touches related objects in a loop uses `select_related` (FK/one-to-one) or `prefetch_related` (many). When you add a template loop or serializer over a queryset, check for N+1 explicitly.
- Use `.exists()` instead of `len()`/truthiness for existence checks, `.count()` instead of `len(qs)`, `.update()`/`.bulk_create()` for bulk writes.
- Wrap multi-step writes in `transaction.atomic()`; trigger side effects (emails, webhooks) via `transaction.on_commit`.
- Enforce invariants in the database: `UniqueConstraint`/`CheckConstraint` in `Meta.constraints`, not just `clean()` methods.
- Custom managers/querysets for reusable filters: `Order.objects.unpaid()` rather than repeating `.filter(status="unpaid")`.
## Migrations
- Never edit an applied migration. To change one that has shipped, add a new migration.
- One logical change per migration; name it: `makemigrations orders -n add_refund_status`.
- Data migrations use `apps.get_model("orders", "Order")` — never import models directly into a migration.
- Adding a non-nullable field to a populated table happens in two deploys: (1) add nullable + backfill, (2) make it non-nullable. Flag this in your summary whenever it applies.
- After model changes, run the `--check --dry-run` command above to confirm nothing is missing.
## Settings
```
config/settings/base.py # everything shared
config/settings/local.py # dev: DEBUG=True, console email
config/settings/production.py
```
- Secrets and per-environment values come from environment variables via `django-environ` — never hardcode them in any settings file.
- New settings go in `base.py` with a safe default; override per environment only when values genuinely differ.
## Tests (pytest-django)
- Plain test functions with the `db` fixture / `@pytest.mark.django_db`; no `TestCase` classes in new code.
- Build objects with `factory_boy` factories in `apps/<app>/tests/factories.py` — never fixtures-as-JSON.
- Test services and selectors directly; test views only for status codes, permissions, and serialization shape.
- Assert query efficiency on hot paths with `django_assert_num_queries`.
Install
drop into your repoSave this to your project or home directory so Claude Code can load it.
path./CLAUDE.md
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…