---
description: Django REST Framework conventions
globs: ["**/*.py"]
alwaysApply: false
---

# Django REST Framework

- Use `ModelViewSet`/`GenericViewSet` + mixins and register with a router. Function views only for one-off endpoints.
- Separate read and write serializers when shapes differ. Validation lives in `validate_<field>` / `validate`; business logic in a `services.py` function the view calls.
- Every view sets `permission_classes` explicitly; default to `IsAuthenticated` in settings. Object-level checks via `has_object_permission`.
- Scope querysets in `get_queryset()` to the requesting user/tenant. Never return `Model.objects.all()` for user data.
- Prevent N+1: `select_related` for FKs, `prefetch_related` for reverse/M2M, and check with `django-debug-toolbar` or `assertNumQueries` in tests.
- Pagination on every list endpoint (global `DEFAULT_PAGINATION_CLASS`). Filtering via `django-filter`.
- Document with `drf-spectacular`; add `@extend_schema` where inference is wrong.
- Wrap multi-write operations in `transaction.atomic()`.
- Throttle auth and expensive endpoints.

## Don't
- Don't use `fields = '__all__'` on serializers that write data.
- Don't put logic in `serializer.save()` overrides that should be a service.

## Done when
`python manage.py test` (or `pytest`) passes, `python manage.py makemigrations --check` is clean, and `ruff check .` passes.
