Repository navigation
Expand file tree
/
Copy pathdjango-rest.mdc
More file actions
134 lines (110 loc) · 10.4 KB
/
Copy pathdjango-rest.mdc
File metadata and controls
134 lines (110 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
---
description: Django REST Framework rules — serializers, pagination, no N+1 queries, proper logging.
globs: ""
alwaysApply: true
---
## File Structure and Organization
- All Django apps must live in `apps/` directory. No top-level app folders. Structure: `apps/{app_name}/models.py`, `apps/{app_name}/views.py`, `apps/{app_name}/serializers.py`, `apps/{app_name}/services.py`, `apps/{app_name}/tests/`.
- Business logic must live in `apps/{app_name}/services.py`. Views are thin routers only. If a view has more than 5 lines of logic, move it to services.
- All database queries must live in `apps/{app_name}/models.py` as custom managers or querysets. Never write `Model.objects.filter()` in views or services.
- Test files must be in `apps/{app_name}/tests/test_{feature}.py`. Never put tests in `tests.py`. Run: `python manage.py test apps.{app_name}.tests.test_{feature}`.
- Serializers must be in `apps/{app_name}/serializers.py`. One file per app. Organize with `# Read serializers` and `# Write serializers` comments.
- Configuration must be in `config/settings/` with `base.py`, `local.py`, `staging.py`, `production.py`. Never import settings directly; use `django.conf.settings`.
- URL routing must be in `apps/{app_name}/urls.py`. Main router in `config/urls.py` includes all app URLs with `path('api/{app_name}/', include('apps.{app_name}.urls'))`.
## Naming Conventions
- Model names: PascalCase, singular. `User`, `BlogPost`, `OrderItem`. Never `Users` or `blog_posts`.
- Model fields: snake_case. `created_at`, `updated_at`, `user_id`. Never `createdAt` or `userId`.
- Serializer names: `{Model}Serializer` for write, `{Model}ReadSerializer` for read-only. `UserSerializer`, `UserReadSerializer`.
- ViewSet names: `{Model}ViewSet`. `UserViewSet`, `BlogPostViewSet`.
- Service class names: `{Model}Service` or `{Feature}Service`. `UserService`, `PaymentProcessingService`.
- Manager names: `{action}Manager` or just `objects`. `PublishedManager`, `ActiveManager`.
- Function names in services: verb_noun. `create_user()`, `send_email()`, `calculate_total()`. Never `user_create()`.
- URL route names: `{app}-{action}`. `user-list`, `user-detail`, `order-create`. Use in `reverse()` calls.
- Variable names: snake_case. `user_id`, `total_amount`, `is_active`. Never camelCase.
- Constants: UPPER_SNAKE_CASE in `apps/{app_name}/constants.py`. `MAX_RETRY_ATTEMPTS`, `DEFAULT_PAGE_SIZE`.
## Database Query Rules
- Every QuerySet must have `.select_related()` or `.prefetch_related()` if it accesses foreign keys or reverse relations. Verify with Django Debug Toolbar query count.
- Never use `objects.all()` without pagination. Always chain `.paginate()` or use `PageNumberPagination` in ViewSet.
- Use custom managers for repeated queries. Example: `class PublishedManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_published=True)`.
- All list endpoints must paginate. Set `pagination_class = PageNumberPagination` on ViewSet. Default page size: 20. Never return unbounded lists.
- Use `only()` or `defer()` to limit fields fetched. `User.objects.only('id', 'email')` for large tables.
- Bulk operations must use `bulk_create()` or `bulk_update()`. Never loop and `.save()`. Batch size: 1000.
- Raw SQL is forbidden. Use ORM. If ORM is insufficient, use `Model.objects.raw()` with parameterized queries only.
- Transactions: wrap multi-step operations in `@transaction.atomic`. Example: `with transaction.atomic(): user.save(); profile.save()`.
## Serializer Rules
- Serializers must validate all input. Use `validate_` methods for field-level, `validate()` for object-level. Never skip validation.
- Read serializers must be read-only: `class Meta: read_only_fields = ['id', 'created_at', 'updated_at']`.
- Write serializers must exclude read-only fields from `fields`. Use `extra_kwargs = {'password': {'write_only': True}}`.
- Nested serializers must use `many=True` for lists. Example: `comments = CommentSerializer(many=True, read_only=True)`.
- Custom validation errors must use `serializers.ValidationError({'field': 'message'})`. Never raise generic exceptions.
- Serializer `create()` and `update()` must call service layer. Never write to database directly. Example: `return UserService.create(validated_data)`.
- Use `SerializerMethodField` only for read-only computed fields. Never use for input.
## View and ViewSet Rules
- All views must be ViewSets. Use `ModelViewSet`, `ReadOnlyModelViewSet`, or custom `ViewSet`. Never use generic `APIView`.
- ViewSet actions must be thin. Max 3 lines. Delegate to services. Example: `def create(self, request): return Response(UserService.create(request.data))`.
- Override `get_queryset()` to apply filters, permissions, and select_related. Never filter in `list()` or `retrieve()`.
- Override `get_serializer_class()` to return different serializers for read vs. write. Example: `return self.ReadSerializer if self.action == 'retrieve' else self.WriteSerializer`.
- Use `@action(detail=False, methods=['post'])` for custom endpoints. Never create extra views.
- All responses must use `Response()` with explicit status codes. `Response(data, status=status.HTTP_201_CREATED)`. Never return raw dicts.
- Pagination must be set on ViewSet: `pagination_class = PageNumberPagination`. Never paginate in views.
## Error Handling
- All exceptions must inherit from `apps.common.exceptions.AppError`. Define custom exceptions in `apps/common/exceptions.py`.
- Every try/except must catch specific exceptions. Never use bare `except:` or `except Exception:`.
- Service layer must raise typed exceptions. Views catch and convert to HTTP responses via exception handlers.
- Define a global exception handler in `config/settings/base.py`: `EXCEPTION_HANDLER = 'apps.common.exceptions.custom_exception_handler'`.
- All database operations must catch `IntegrityError` and `ValidationError` explicitly. Convert to `AppError` with user-friendly message.
- API responses for errors must follow format: `{'error': 'code', 'message': 'user message', 'details': {...}}`. Use `apps.common.serializers.ErrorSerializer`.
- Never log raw exceptions. Use `logger.exception()` with context: `logger.exception('Failed to create user', extra={'user_id': user_id})`.
## Logging
- Replace all `print()` with `logger.info()`, `logger.warning()`, `logger.error()`. Import: `import logging; logger = logging.getLogger(__name__)`.
- Log at function entry for long operations: `logger.info('Starting user import', extra={'count': len(users)})`.
- Log at function exit with result: `logger.info('User created', extra={'user_id': user.id})`.
- Log errors with full context: `logger.error('Payment failed', extra={'order_id': order.id, 'error': str(e)})`.
- Never log sensitive data: passwords, tokens, SSNs, credit cards. Use `***` masking.
- Use structured logging. Pass `extra={}` dict, never f-strings in message. Example: `logger.info('Event', extra={'key': value})`.
## Security
- Environment variables must be loaded in `config/settings/base.py` using `os.getenv()`. Never hardcode secrets.
- Sensitive settings: `SECRET_KEY`, `DATABASE_PASSWORD`, `API_KEYS` must be in `.env` file (gitignored). Use `python-dotenv`.
- All user input must be validated at serializer layer. Use `validators.URLValidator()`, `validators.EmailValidator()`, Zod-like validation.
- SQL injection: use parameterized queries only. Never f-string SQL. Use ORM or `.raw()` with `%s` placeholders.
- CSRF protection: enabled by default. Never disable `CsrfViewMiddleware`.
- Authentication: use `TokenAuthentication` or `JWTAuthentication`. Never use session auth for APIs.
- Permissions: use `IsAuthenticated`, `IsAdminUser`, or custom permission classes. Set on ViewSet: `permission_classes = [IsAuthenticated]`.
- Rate limiting: use `throttle_classes = [UserRateThrottle]`. Configure in settings: `REST_FRAMEWORK['DEFAULT_THROTTLE_RATES'] = {'user': '100/hour'}`.
## Testing
- Test files: `apps/{app_name}/tests/test_{feature}.py`. Use `TestCase` from `django.test`.
- Every model must have a factory in `apps/{app_name}/tests/factories.py` using `factory_boy`. Example: `class UserFactory(factory.django.DjangoModelFactory): class Meta: model = User`.
- Test naming: `test_{action}_{condition}_{expected_result}`. Example: `test_create_user_with_valid_email_returns_201`.
- Every service function must have unit tests. Mock database calls. Use `@patch('apps.user.models.User.objects.create')`.
- Every ViewSet action must have integration tests. Use `APITestCase`, `APIClient`. Test with authenticated and unauthenticated users.
- Test database queries: use `assertNumQueries(n)` context manager. Verify select_related/prefetch_related work.
- Never use fixtures. Use factories. Fixtures are brittle and slow.
- Test error cases: invalid input, permissions denied, not found, conflicts. Verify correct status codes and error messages.
- Coverage minimum: 80%. Run: `coverage run --source='apps' manage.py test && coverage report`.
## Forbidden Patterns
- Never use `Model.objects.all()` without pagination or filtering.
- Never write business logic in views. Move to services.
- Never use `as` type assertions. Use type guards or validation.
- Never catch generic `Exception`. Catch specific exceptions.
- Never use `print()`. Use logger.
- Never hardcode secrets in code or settings files.
- Never use raw SQL. Use ORM.
- Never loop and `.save()`. Use `bulk_create()` or `bulk_update()`.
- Never return raw model instances. Serialize with DRF serializers.
- Never use `objects.all()` in templates. Prefetch in view.
- Never disable CSRF protection.
- Never use session authentication for APIs.
- Never skip input validation.
- Never log sensitive data.
- Never use `*` imports. Import explicitly: `from apps.user.models import User`.
## Required Decorators and Patterns
- All service functions that modify state must be wrapped in `@transaction.atomic`.
- All ViewSet list actions must have `@action(detail=False, methods=['get'])` or inherit from `ModelViewSet`.
- All custom endpoints must use `@action(detail=False/True, methods=['post'/'get'/'patch'])`.
- All async tasks must use Celery with `@shared_task` decorator. Never use threading.
- All time-based fields must use `auto_now_add=True` for created, `auto_now=True` for updated.
- All soft-delete models must use `is_deleted` boolean field and custom manager filtering.
---
> Source: [Codelibrium](https://codelibrium.com) — the marketplace for AI behaviour files.
> Browse multiple rulesets at [codelibrium.com](https://codelibrium.com) or install via CLI:
> `npx codelibrium-cli install <ruleset-name>`