# Django — Claude Code rules Follow Django's official coding style, ORM optimization, and security guidance. Rules below are drawn from the Django docs; don't invent conventions. ## Style - Follow PEP 8. Format all code with **black** (88-char line limit in code; wrap docs/comments/docstrings at 79). - 4 spaces for indentation in Python; 2 spaces in HTML/templates. - `underscore_case` for variables, functions, methods (`poll.get_unique_voters()`, not `getUniqueVoters()`). `InitialCaps` for class names. - String interpolation: %-formatting, f-strings, or `str.format()` — whichever is most readable. Don't put calls or arithmetic inside an f-string (`f"hello {get_user()}"` is out); assign to a local first. - Never use f-strings for anything translatable (error/log messages). Use `format()` and mark strings for i18n. - In views, the first parameter is always `request`, never `req`. - Remove unused imports and trailing whitespace. Don't put your name in code. ## Imports - Sort imports with **isort**. Group order: future, standard library, third-party, other Django components, local component, try/except. Alphabetize each group by full module name; put `import module` before `from module import …`. - Absolute imports for other Django components; one-dot relative (`from .models import …`) for local. Avoid multi-dot relative imports. - Use convenience imports when available: `from django.views import View`, not the deep path. ## Models & ORM - Field names are all lowercase with underscores. Put `class Meta` *after* the fields, separated by one blank line. - Member ordering: fields → custom manager attrs → `Meta` → `__str__`/magic methods → `save()` → `get_absolute_url()` → custom methods. - Define `choices` as a mapping with all-uppercase class-attribute names, or use `models.TextChoices`/`IntegerChoices`. - **Avoid N+1.** Use `select_related()` for ForeignKey / OneToOne (SQL JOIN); use `prefetch_related()` for ManyToMany and reverse FK/O2O (separate queries joined in Python). - QuerySets are lazy and cache results once evaluated. Store `group.members.all()` in a variable and reuse it rather than re-querying. - Use `count()` / `exists()` / `len()` for their specific intent — but if you need the rows too, evaluate the QuerySet once instead of calling `exists()` then `all()` then `count()`. - Use `values()` / `values_list()` when you only need dicts/lists, not model objects. Use `only()` / `defer()` to skip heavy columns (profile first). - Do work in the DB: `filter()`/`exclude()` for filtering, `F()` expressions for same-row fields, `annotate()` for aggregation. Prefer `QuerySet.update()` / `bulk_create()` / `bulk_update()` and bulk M2M `add()`/`remove()` over per-object `save()` loops. - Access the FK id directly (`entry.blog_id`) instead of `entry.blog.id` to avoid a fetch. Add `Meta.indexes` / `db_index` for frequently filtered fields; profile with `QuerySet.explain()`. ## Security - Keep `{% csrf_token %}` in every POST form and keep `CsrfViewMiddleware` enabled. Use `@csrf_exempt` only when absolutely necessary. - Rely on the ORM's query parameterization for SQL safety. Use `raw()` / `extra()` / `RawSQL()` sparingly and always escape user-controlled parameters. - Let template auto-escaping protect against XSS. Be very careful with `mark_safe`, the `safe` tag, `is_safe`, or autoescape-off, and with storing/rendering user HTML. - Set `ALLOWED_HOSTS` explicitly. Read the host via `request.get_host()`, not `request.META` directly (which bypasses validation). - In production: `DEBUG = False`, keep `SECRET_KEY` secret, deploy behind HTTPS with `SECURE_SSL_REDIRECT`, `SESSION_COOKIE_SECURE`, `CSRF_COOKIE_SECURE`, and HSTS (`SECURE_HSTS_SECONDS` etc.). - Keep clickjacking protection (`X-Frame-Options` middleware) on. Validate all user input via forms. Limit upload/request body size at the web server; serve user-uploaded content from a separate domain. ## Don't - Don't access `django.conf.settings` at module top level (import time) — it breaks `settings.configure()`. Use lazy indirection (`LazyObject`, `lazy()`, or `lambda`). - Don't disable CSRF, escaping, or `ALLOWED_HOSTS` validation for convenience. - Don't loop and `.save()` when a single `update()` / `bulk_*` call works. - Don't query inside a loop when `select_related` / `prefetch_related` would fetch it all at once. - Don't order results (`Meta.ordering`) when you don't need them — call `order_by()` with no args to drop it.