BoilerplateHub

Claude Code Setup for Django

Rules file: CLAUDE.md

Django's ORM is the centre of gravity: it is expressive enough that an agent can write something readable and correct-looking that issues hundreds of queries. An agent working here needs to know the Django version, whether the project uses function based or class based views, how settings are split across environments, and that any model edit implies a migration. Async support exists but is partial, so the boundary between sync ORM code and async views is a real source of runtime failures rather than a style question.

Configuring Claude Code for Django

  • State the Django version and whether views are function based or class based, since agents otherwise alternate between the two in one codebase.
  • Document how settings are split, base plus per environment modules or a single settings file with env lookups, so the agent edits the right one.
  • Put manage.py test or your pytest command in the commands section, and mention that makemigrations must be run after model edits.

What to put in CLAUDE.md

The content is the same across agents, only the filename differs. Copy this, adapt the commands to your repository, and save it as CLAUDE.md.

CLAUDE.md
Download
# AGENTS.md

This is a subscription SaaS built as a Django monolith with a REST API, background jobs, and Stripe billing.

## Project

Stack: Python 3.12, Django 5, Django REST Framework, PostgreSQL, Celery with Redis, Stripe, pytest-django, ruff.

Layout:

- `config/settings/base.py`, `config/settings/dev.py`, `config/settings/prod.py`
- `config/urls.py` root URL configuration
- `apps/accounts/` custom user model, authentication, teams
- `apps/billing/` Stripe customers, subscriptions, webhook handling
- `apps/core/` shared abstract models, mixins, and utilities
- `apps/<feature>/` one Django app per bounded feature
- Each app: `models.py`, `views.py`, `serializers.py`, `services.py`, `tasks.py`, `urls.py`, `admin.py`, `migrations/`
- `templates/` server rendered pages, `static/` assets

Entry points: `manage.py`, `config/settings/base.py`, `config/urls.py`, `apps/billing/webhooks.py`.

## Commands

```bash
uv sync                                        # install dependencies
uv run python manage.py runserver              # dev server on :8000
uv run python manage.py makemigrations         # create migrations
uv run python manage.py migrate                # apply migrations
uv run python manage.py showmigrations         # inspect state
uv run python manage.py shell_plus             # interactive shell
uv run python manage.py createsuperuser
uv run python manage.py collectstatic --noinput
uv run pytest                                  # test suite
uv run pytest apps/billing -k webhook          # one app or test
uv run ruff check . && uv run ruff format .
uv run celery -A config worker -l info         # background worker
uv run celery -A config beat -l info           # scheduled jobs
```

`DJANGO_SETTINGS_MODULE` defaults to `config.settings.dev` locally.

## Code style

- Business logic lives in `services.py`, not in views and not in serializers.
- Views are thin: permission check, deserialize, call a service, return a response.
- Query in the view or service, never in a template. Use `select_related` and `prefetch_related` for every relation you render.
- Custom managers and querysets for reusable filters. No copy pasted `filter()` chains.
- Model methods that mutate state wrap their writes in `transaction.atomic`.
- Use `get_user_model()`, never import the user model directly.
- Money is a `DecimalField` with explicit precision, never a float.
- Signals are avoided. If a side effect matters, call it explicitly from a service.
- Type hints on service functions. Line length 100, enforced by ruff.
- Every `TextChoices` and `IntegerChoices` is defined on the model, not as loose constants.

## Boundaries

Do not touch without explicit instruction:

- Existing files in any `migrations/` directory. Never edit, squash, or delete a migration that has been applied.
- `config/settings/prod.py` and any environment specific credential.
- `uv.lock`, `.env`, `.github/workflows/`, deployment manifests.
- `apps/billing/webhooks.py` and Stripe price configuration.

Needs human review: data migrations, changes to permission classes, anything that touches subscription state, and any change to the custom user model.

Generate migrations when models change, show the resulting file, and stop before applying to anything but the local database.

## Testing

- `uv run pytest` runs the suite, `uv run pytest apps/<app>` runs one app.
- Required tests: permission classes, service functions, Celery tasks, and every billing state transition.
- Use `pytest.mark.django_db` and the factories in `apps/<app>/tests/factories.py`. Do not add JSON fixtures.
- API tests go through `APIClient` and assert status code and response shape.
- Celery tasks are tested by calling the function directly, with `CELERY_TASK_ALWAYS_EAGER` reserved for integration tests.
- A bug fix ships with a failing-first regression test.

## Git workflow

- Branch from `main`: `feat/short-description`, `fix/short-description`.
- Conventional commits with the app as scope: `feat(billing): handle trial expiry`.
- Before creating a migration, rebase on `main` so the revision ordering stays linear. If two migrations collide, regenerate rather than hand merging.
- Code and its migration ship in the same commit.
- PR description lists migrations, new settings, and any manual step needed at deploy.

Rule these out for Django

These are the failures that repeat across sessions, so each one belongs in CLAUDE.md.

Queries that N+1 without select_related

An agent writes a view that returns Order.objects.all() and a template that renders order.customer.email, which issues one additional query per order. The code reads well and passes tests against a fixture of three rows. Add to your rules file: 'Any queryset that feeds a loop or a serializer must use select_related for forward foreign keys and prefetch_related for reverse and many to many relations. Assert query counts with assertNumQueries in tests for list endpoints.'

Changing models without generating a migration

Agents edit models.py, confirm the code looks right, and stop, leaving the database schema behind. Worse, some hand-write a migration file with an invented dependency graph rather than running makemigrations. Write: 'Every change to a models.py file is followed by python manage.py makemigrations and the generated file is committed. Never hand-author a migration except for a deliberate RunPython data migration.'

Filtering in Python instead of in the database

Agents call list() on a queryset or iterate it and then use a comprehension to filter, or use len(qs) where qs.count() belongs, pulling every row into memory to find a handful. Querysets are lazy and chainable specifically so this is unnecessary. Add: 'Do filtering, ordering, aggregation, and counting with queryset methods. Never load a queryset into a list to filter it, and use .count() and .exists() rather than len() and truthiness.'

Hardcoding settings and loosening security defaults

When something fails locally an agent will set DEBUG to True, add a wildcard to ALLOWED_HOSTS, or paste a SECRET_KEY literal into settings.py to make the traceback go away. Those edits then get committed. Make it a hard rule: 'Settings values come from environment variables. Never edit DEBUG, ALLOWED_HOSTS, SECRET_KEY, or any SECURE_ setting to resolve an error, and never commit a literal secret to a settings module.'

Wiring side effects through post_save signals

Asked to send a welcome email on signup, an agent adds a post_save receiver, which fires from fixtures, from bulk loads, and from every test that creates a user, and hides the control flow from anyone reading the view. Add: 'Side effects such as email, billing, and external API calls are called explicitly from a service function, not from model signals. Signals are reserved for cache invalidation and similar cross-cutting concerns.'

Calling the sync ORM from an async view

Agents convert a view to async def because it feels modern, then call the regular ORM inside it, which raises SynchronousOnlyOperation, or they wrap everything in sync_to_async and gain nothing but overhead. Async is only worth it when the view is genuinely IO bound on something external. Write: 'Views are sync by default. If a view is async, ORM access uses the async queryset methods or sync_to_async explicitly, and the reason for going async is stated in a comment.'

Skills that pair with this setup

Same framework, other agents