All Django templates

Django + DRF Rules

Django REST Framework conventions: viewsets, serializers, permissions.

DevZone Tools2,340 copiesUpdated Apr 4, 2026DjangoPython
# CLAUDE.md — Django + DRF

## Apps

- One Django app per bounded context. Don't make a single `core` app that owns everything.
- App layout:
  ```
  myapp/
    models.py
    serializers.py
    views.py        # ViewSets / APIViews
    urls.py
    permissions.py
    services.py     # business logic
    selectors.py    # read-only queries
    tests/
  ```
- Business logic lives in `services.py` (writes) and `selectors.py` (reads). Views call services. Models stay thin.

## Serializers

- One serializer per output shape. Don't reuse the same serializer for list and detail when fields differ — add a `ListSerializer` and `DetailSerializer`.
- Validate at the serializer (`validate_<field>`, `validate`). Don't validate inside views.
- Use `source=` to map field names; don't rename in the model just to please an API contract.
- For nested writes, override `create`/`update` explicitly. Don't rely on `WritableNestedSerializer` magic.

## ViewSets

- `ModelViewSet` for full CRUD. `ReadOnlyModelViewSet` for read-only resources. Don't use generic mixins by hand when a ViewSet does it.
- Override `get_queryset` to scope by user/tenant — the base `queryset` attribute should be the unscoped default.
- Override `get_serializer_class` to switch between list/detail/write serializers.
- Custom actions with `@action(detail=True/False, methods=[...])` — never overload PATCH for "do this thing".

## Permissions

- Default to `[IsAuthenticated]` globally in `REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES']`.
- Per-view permissions are subclasses of `BasePermission`. Combine with `&`, `|`, `~`.
- Object-level checks go in `has_object_permission`. Don't duplicate them in `get_queryset` and pretend it's enough — it isn't for direct lookups.

## Pagination & filtering

- Set `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE`. Never return unpaginated lists from production endpoints.
- Use `django-filter` for query-string filters. Don't write `if request.GET.get(...)` chains.
- For sort, use DRF's `OrderingFilter` with an explicit `ordering_fields` allowlist.

## Errors

- Raise DRF exceptions (`NotFound`, `PermissionDenied`, `ValidationError`). Don't return `Response({'error': ...}, status=...)` from view code.
- Custom exceptions inherit from `APIException` with `status_code` and `default_detail`.

## Don't

- Don't put business logic in the serializer. Serializers shape and validate; services act.
- Don't use `select_related` / `prefetch_related` only on the detail endpoint. Profile the list endpoint.
- Don't expose internal field names (`is_deleted`, `internal_notes`) without filtering with serializer `fields` or `exclude`.
- Don't disable CSRF on session-authenticated endpoints.

Other Django templates