К основному содержанию
правило

django-rest-framework — правило для Cursor: стандарты DRF-API

Правило Cursor, которое задаёт агенту единый стиль разработки API на Django REST Framework: структура проекта, сериализаторы, ViewSets.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/django-rest-framework.mdc
Открыть на GitHub

Что делает django-rest-framework

Файл .cursor/rules/django-rest-framework.mdc подключается к Cursor и действует на все .py-файлы проекта. Внутри — набор требований к API на Django REST Framework: где держать логику, как настраивать глобальные параметры, какие классы использовать для CRUD. Агент читает эти требования и предлагает код в том же стиле, а не каждый раз новый.

Структурно правило требует отдельного Django-приложения api, файла api_settings.py с настройками DRF и DefaultRouter в urls.py. Для сериализаторов — ModelSerializer для CRUD и кастомный Serializer для нестандартных данных, плюс select_related и prefetch_related, чтобы не ловить N+1 запросы. Для эндпоинтов — ModelViewSet и ReadOnlyModelViewSet. В примерах настроек фигурируют rest_framework_simplejwt, django_filters и drf_spectacular — если у вас их нет, часть советов агента придётся адаптировать под свой стек.

Правило жёсткое: оно диктует конкретную раскладку файлов и конкретные библиотеки. В проекте, где API уже разложен иначе, агент начнёт предлагать переносы и переименования — это повод проверить, стоит ли принимать такие правки.

Какие задачи решает

  • задать агенту единый стиль кода для API на DRF
  • разложить эндпоинты по ViewSets и DefaultRouter вместо ручных APIView
  • убрать N+1 запросы через select_related и prefetch_related
  • вынести глобальные настройки DRF в отдельный файл
  • подключить JWT-аутентификацию и фильтрацию в настройках

Примеры запросов агенту

  • Создай ViewSet для модели Product по правилам проекта
  • Разложи сериализаторы для Category и Review так, как принято в этом репозитории
  • Настрой api_settings.py и подключи его в settings.py

Как установить django-rest-framework

Вручную: положить файлы

.cursor/rules/django-rest-framework.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о django-rest-framework

Как установить django-rest-framework в Cursor?

Создайте файл .cursor/rules/django-rest-framework.mdc в корне проекта и вставьте в него содержимое правила. Cursor подхватит его автоматически.

Что делает это правило?

Задаёт агенту стандарты разработки API на Django REST Framework: структуру приложения api, работу с сериализаторами и ViewSets, настройки аутентификации и пагинации.

Нужны ли дополнительные библиотеки?

В примерах правила используются rest_framework_simplejwt, django_filters и drf_spectacular. Без них часть предлагаемого кода не заработает.

Исходный текст

django-rest-framework.mdc · CC0-1.0
---
description: Enforces modern, secure, and performant `django-rest-framework` best practices for API development, focusing on maintainability and scalability.
globs: **/*.py
---
# django-rest-framework Best Practices

This guide outlines our definitive standards for building APIs with Django REST Framework (DRF). Adhere to these rules to ensure consistency, security, and performance across all projects.

## 1. Code Organization and Structure

Always structure your API within a dedicated Django app, typically named `api`. Centralize DRF-specific settings and use `DefaultRouter` for URL management.

- **Dedicated `api/` app**: Isolate API logic.
- **`api_settings.py`**: Store all DRF global settings here, then import into `settings.py`.
- **`urls.py` with `DefaultRouter`**: Automate URL patterns for ViewSets.

```python
# project_root/api/apps.py
from django.apps import AppConfig

class ApiConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'api'

# project_root/api/settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 25,
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', # For OpenAPI
}

# project_root/settings.py
from .api.settings import REST_FRAMEWORK as API_REST_FRAMEWORK
REST_FRAMEWORK = API_REST_FRAMEWORK
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework_simplejwt',
    'django_filters',
    'drf_spectacular', # For OpenAPI
    'api',
]

# project_root/api/urls.py
from rest_framework.routers import DefaultRouter
from .views import UserViewSet, ProductViewSet
from django.urls import path, include

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'products', ProductViewSet, basename='product')

urlpatterns = [
    path('', include(router.urls)),
]

# project_root/project_name/urls.py
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('api.urls')), # Versioned API namespace
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]
```

## 2. Serializers: Efficiency and Validation

Prioritize `ModelSerializer` for CRUD operations. Use custom `Serializer` classes only for complex, non-model-backed data or advanced validation. Always use `select_related`/`prefetch_related` to prevent N+1 queries.

```python
from rest_framework import serializers
from django.db.models import QuerySet
from typing import Any

from .models import Product, Category, Review, User

# ❌ BAD: N+1 queries for related objects, exposing all fields
class BadProductSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source='category.name')
    reviews = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = ['id', 'name', 'price', 'category', 'reviews']

    def get_reviews(self, obj: Product) -> list[dict[str, Any]]:
        return [{'text': r.text, 'rating': r.rating} for r in obj.reviews.all()]

# ✅ GOOD: Optimized queries, controlled field exposure, explicit types
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name']

class ReviewSerializer(serializers.ModelSerializer):
    user = serializers.SlugRelatedField(slug_field='username', read_only=True)

    class Meta:
        model = Review
        fields = ['id', 'user', 'text', 'rating', 'created_at']
        read_only_fields = ['user', 'created_at']

class ProductSerializer(serializers.ModelSerializer):
    category: CategorySerializer = CategorySerializer(read_only=True) # Type hint for nested serializer
    reviews: ReviewSerializer = ReviewSerializer(many=True, read_only=True) # Type hint for nested list

    class Meta:
        model = Product
        fields = ['id', 'name', 'price', 'category', 'reviews', 'stock_count']
        read_only_fields = ['stock_count'] # Fields that should not be updated via API

    def create(self, validated_data: dict[str, Any]) -> Product:
        # Custom logic if needed, e.g., setting owner
        validated_data['owner'] = self.context['request'].user
        return super().create(validated_data)

# ✅ GOOD: Custom serializer for non-model data or specific input validation
class UserRegistrationSerializer(serializers.Serializer):
    username: str = serializers.CharField(max_length=150)
    email: str = serializers.EmailField()
    password: str = serializers.CharField(write_only=True)
    password_confirm: str = serializers.CharField(write_only=True)

    def validate(self, data: dict[str, Any]) -> dict[str, Any]:
        if data['password'] != data['password_confirm']:
            raise serializers.ValidationError({"password_confirm": "Passwords do not match."})
        return data

    def create(self, validated_data: dict[str, Any]) -> User:
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password']
        )
        return user
```

## 3. ViewSets & Routers: Streamlining Endpoints

Always use `ModelViewSet` for standard CRUD operations. For read-only resources, use `ReadOnlyModelViewSet`. Leverage `DefaultRouter` to automatically generate URLs.

```python
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from django.db.models import QuerySet
from typing import Any

from .models import Product, Category, User
from .serializers import ProductSerializer, CategorySerializer, UserRegistrationSerializer, ReviewSerializer

# ❌ BAD: Manual URL routing, repetitive logic for each HTTP method
# class ProductList(APIView):
#     def get(self, request): ...
#     def post(self, request): ...

# ✅ GOOD: ModelViewSet for full CRUD, optimized queryset, explicit permissions
class ProductViewSet(viewsets.ModelViewSet):
    queryset: QuerySet[Product] = Product.objects.select_related('category').prefetch_related('reviews__user').all()
    serializer_class: type[ProductSerializer] = ProductSerializer
    permission_classes: list[type[IsAuthenticated | IsAdminUser]] = [IsAuthenticated, IsAdminUser] # Example: Admin can manage products

    def get_queryset(self) -> QuerySet[Product]:
        # Optimize queryset for specific actions if needed
        if self.action == 'list':
            return self.queryset.filter(is_active=True)
        return self.queryset

    def perform_create(self, serializer: ProductSerializer) -> None:
        serializer.save(owner=self.request.user) # Automatically assign owner

    @action(detail=True, methods=['post'], permission_classes=[IsAuthenticated])
    def add_review(self, request: Any, pk: int | None = None) -> Response:
        product = self.get_object()
        serializer = ReviewSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save(product=product, user=request.user)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

# ✅ GOOD: ReadOnlyModelViewSet for read-only resources
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
    queryset: QuerySet[Category] = Category.objects.all()
    serializer_class: type[CategorySerializer] = CategorySerializer
    permission_classes: list[type[IsAuthenticated]] = [IsAuthenticated]

# ✅ GOOD: Custom ViewSet for non-model operations (e.g., user registration)
class UserViewSet(viewsets.ViewSet):
    permission_classes: list[type[IsAuthenticated]] = [IsAuthenticated] # Adjust as needed for registration

    @action(detail=False, methods=['post'], permission_classes=[]) # No auth for registration
    def register(self, request: Any) -> Response:
        serializer = UserRegistrationSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        return Response({"message": "User registered successfully."}, status=status.HTTP_201_CREATED)

    @action(detail=False, methods=['get'])
    def me(self, request: Any) -> Response:
        serializer = ProductSerializer(request.user) # Example: reuse ProductSerializer for user details
        return Response(serializer.data)
```

## 4. Authentication & Permissions: Secure Access

Always use token-based authentication (JWT or DRF's `TokenAuthentication`) for stateless APIs. Implement granular permissions using DRF's built-in classes or custom ones. Never use `AllowAny` unless the endpoint is truly public.

```python
from rest_framework.permissions import BasePermission, SAFE_METHODS
from rest_framework.request import Request
from typing import Any

# ✅ GOOD: Custom object-level permission
class IsOwnerOrReadOnly(BasePermission):
    """
    Object-level permission to allow only owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """
    def has_object_permission(self, request: Request, view: Any, obj: Any) -> bool:
        if request.method in SAFE_METHODS:
            return True
        return obj.owner == request.user

# Apply to ViewSet:
# class ProductViewSet(viewsets.ModelViewSet):
#     permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
```

## 5. Pagination, Filtering & Versioning

Enable `PageNumberPagination` (default) or `CursorPagination` for large datasets. Use `django-filter` for query parameter filtering. Implement URL versioning (`/api/v1/`) for clear API evolution.

```python
# project_root/api/filters.py
import django_filters
from .models import Product

class ProductFilter(django_filters.FilterSet):
    min_price = django_filters.NumberFilter(field_name="price", lookup_expr='gte')
    max_price = django_filters.NumberFilter(field_name="price", lookup_expr='lte')
    category_name = django_filters.CharFilter(field_name="category__name", lookup_expr='icontains')

    class Meta:
        model = Product
        fields = ['category', 'is_active']

# Apply to ViewSet:
# class ProductViewSet(viewsets.ModelViewSet):
#     filterset_class = ProductFilter
```

## 6. Performance Considerations

Prevent N+1 queries by using `select_related` and `prefetch_related` in your `get_queryset` method.

```python
# ✅ GOOD: Optimized queryset in ViewSet
class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get_queryset(self) -> QuerySet[Product]:
        # Always optimize for common list/retrieve actions
        return self.queryset.select_related('category').prefetch_related('reviews__user')
```

## 7. Error Handling

Leverage DRF's built-in exception handling. For custom errors, define a custom exception handler that returns consistent, machine-readable responses.

```python
# project_root/api/utils.py
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from typing import Any

def custom_exception_handler(exc: Exception, context: dict[str, Any]) -> Response | None:
    # Call DRF's default exception handler first
    response = exception_handler(exc, context)

    if response is not None:
        # Example: Customize validation error messages
        if response.status_code == status.HTTP_400_BAD_REQUEST and 'detail' not in response.data:
            response.data = {'errors': response.data}
        return response

    # Handle unhandled exceptions gracefully
    return Response(
        {'detail': 'An unexpected error occurred.'},
        status=status.HTTP_500_INTERNAL_SERVER_ERROR
    )

# project_root/api/settings.py
REST_FRAMEWORK = {
    # ...
    'EXCEPTION_HANDLER': 'api.utils.custom_exception_handler',
}
```

## 8. Type Hints

Mandate type hints for all DRF components: serializers, views, viewsets, and custom permissions. This improves code readability, maintainability, and enables static analysis.

```python
# All examples above demonstrate mandatory type hints.
# Example:
# class ProductSerializer(serializers.ModelSerializer):
#     category: CategorySerializer = CategorySerializer(read_only=True)
#     def create(self, validated_data: dict[str, Any]) -> Product: ...
```

## 9. Rate Limiting

Implement DRF's throttling classes to protect your API from abuse.

```python
# project_root/api/settings.py
REST_FRAMEWORK = {
    # ...
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
        'burst': '60/min', # Custom throttle rate
    }
}

# Apply to ViewSet for custom throttling:
# class ProductViewSet(viewsets.ModelViewSet):
#     throttle_classes = [UserRateThrottle, AnonRateThrottle] # Or custom throttle
#     throttle_scope = 'burst' # Use the 'burst' rate defined in settings
```

## 10. Async Support

For high-throughput endpoints, isolate async code within `async def` views or specific `ViewSet` actions. Run your project with an ASGI server (Uvicorn/Daphne). DRF's native async support is evolving; consider `adrf` for full async ViewSets if truly necessary, but be aware it's still experimental.

```python
from rest_framework.views import APIView
from rest_framework.response import Response
import asyncio
import httpx # For async HTTP requests
from typing import Any

# ✅ GOOD: Isolate async logic to specific views/actions
class AsyncDataView(APIView):
    async def get(self, request: Any, *args: Any, **kwargs: Any) -> Response:
        # Simulate an async operation, e.g., calling an external async service
        async with httpx.AsyncClient() as client:
            response = await client.get("https://api.example.com/external-data")
            response.raise_for_status()
            data = response.json()
        return Response({"message": "Data fetched asynchronously", "data": data})

# project_root/api/urls.py
# from .views import AsyncDataView
# urlpatterns = [
#     path('async-data/', AsyncDataView.as_view()),
#     # ...
# ]
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «Бэкенд и API» →
django-rest-api-development Mindrally Скилл задаёт агенту правила разработки REST API на Django: структура проекта, сериализаторы, JWT-аутентификация и работа с запросами к базе. CX Codex +1 868установок rest-api-django Mindrally Набор правил для ИИ-агента, который пишет Django REST API: структура проекта, сериализаторы, JWT-аутентификация, борьба с N+1. CX Codex +1 712установок cdrf-expert vintasoftware Помогает проектировать и отлаживать классовые представления Django REST Framework, опираясь на структуру cdrf.co. Выбирает базовые классы и точные точки переопределения методов. CX Codex +1 516установок django-expert vintasoftware Подсказывает агенту, как писать модели, вьюхи, сериализаторы DRF и оптимизировать запросы ORM по практикам Django. CX Codex +1 5 700установок django-patterns affaan-m Помогает проектировать архитектуру Django и DRF: настраивать раздельные конфигурации, писать кастомные QuerySet и организовывать структуру приложений. CX Codex +1 5 200установок django-security affaan-m Скилл с правилами безопасности Django: настройки продакшена, аутентификация, права доступа, защита от SQL-инъекций и XSS. Пригодится при ревью и деплое. CX Codex +1 5 000установок

Из того же набора69

ffmpeg Правило для Cursor, которое задаёт агенту порядок работы с FFmpeg: вызовы через libav*, двухпроходное кодирование и закреплённые сборки в CI. ★ 3 579звёзд репо esbuild Правило для Cursor, которое задаёт агенту порядок работы с esbuild: один конфиг вместо россыпи флагов в package.json. Пригодится, когда сборка уже есть, но её никто не может поддерживать. ★ 3 579звёзд репо electron Правило для Cursor задаёт команде единый стиль разработки на Electron: контекстная изоляция, безопасный contextBridge, IPC через invoke и кросс-платформенные пути. ★ 3 579звёзд репо drizzle Правило задаёт агенту Cursor стиль работы с Drizzle ORM: модульные схемы, явные ограничения полей и единый нейминг в TypeScript-проекте. ★ 3 579звёзд репо digitalocean Правило для Cursor задаёт, как строить и разворачивать приложения в DigitalOcean: VPC, managed-сервисы, контейнеры. ★ 3 579звёзд репо datadog Правило для Cursor задаёт стандарты Datadog: обязательные теги service, env и version, структурные логи и порядок в именах метрик. ★ 3 579звёзд репо d3 Правило для Cursor задаёт, как писать код визуализаций на D3.js v7+: модульные импорты, selection.join() вместо ручного enter/update/exit, стили через CSS. ★ 3 579звёзд репо cuda Правило задаёт Cursor стиль работы с CUDA C++: проверка ошибок API, разделение host и device кода, оптимизация ядер. Пригодится, если пишете GPU-код и устали от молчаливых падений. ★ 3 579звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой