6.4 KiB
6.4 KiB
Example: Django REST API Skill
This is a complete example of a Fullstack skill for Django REST API development.
---
name: django-api
description: Creates Django REST Framework endpoints with serializers, views, and URL routing. Use when building REST APIs in Django.
argument-hint: [resource-name] [fields...]
allowed-tools: Read, Write, Edit, Glob, Grep
---
# Django REST API Generator
Generate production-ready Django REST Framework endpoints with proper serializers, views, URL routing, and tests.
## When to Use
- Creating new API endpoints for a resource
- Adding CRUD operations to an existing model
- Setting up API authentication and permissions
- Implementing filtering, pagination, and search
## Prerequisites
- Django REST Framework installed (`djangorestframework`)
- Model exists or will be created
- URL configuration set up for API routes
## Instructions
### Step 1: Analyze the Model
Read the existing model or create one based on the resource name and fields provided in `$ARGUMENTS`.
### Step 2: Create Serializer
Generate a serializer in `serializers.py`:
```python
from rest_framework import serializers
from .models import ResourceName
class ResourceNameSerializer(serializers.ModelSerializer):
class Meta:
model = ResourceName
fields = ['id', 'field1', 'field2', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
def validate_field1(self, value):
"""Add custom validation if needed."""
return value
Step 3: Create Views
Generate function-based views in views_func.py:
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from .models import ResourceName
from .serializers import ResourceNameSerializer
@extend_schema(
request=ResourceNameSerializer,
responses={201: ResourceNameSerializer},
tags=['resource-name'],
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_resource(request):
"""Create a new resource."""
serializer = ResourceNameSerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@extend_schema(
responses={200: ResourceNameSerializer(many=True)},
tags=['resource-name'],
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_resources(request):
"""List all resources for the authenticated user."""
resources = ResourceName.objects.filter(created_by=request.user)
serializer = ResourceNameSerializer(resources, many=True)
return Response(serializer.data)
@extend_schema(
responses={200: ResourceNameSerializer},
tags=['resource-name'],
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_resource(request, pk):
"""Get a single resource by ID."""
try:
resource = ResourceName.objects.get(pk=pk, created_by=request.user)
except ResourceName.DoesNotExist:
return Response(
{'error': 'Resource not found'},
status=status.HTTP_404_NOT_FOUND
)
serializer = ResourceNameSerializer(resource)
return Response(serializer.data)
Step 4: Configure URLs
Add URL patterns in urls.py:
from django.urls import path
from . import views_func
urlpatterns = [
path('resources/', views_func.list_resources, name='resource-list'),
path('resources/create/', views_func.create_resource, name='resource-create'),
path('resources/<int:pk>/', views_func.get_resource, name='resource-detail'),
]
Step 5: Add Tests
Generate tests in tests/test_api.py:
import pytest
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from .factories import ResourceNameFactory, UserFactory
@pytest.fixture
def api_client():
return APIClient()
@pytest.fixture
def authenticated_client(api_client):
user = UserFactory()
api_client.force_authenticate(user=user)
return api_client, user
class TestResourceAPI:
@pytest.mark.django_db
def test_create_resource(self, authenticated_client):
client, user = authenticated_client
url = reverse('resource-create')
data = {'field1': 'value1', 'field2': 'value2'}
response = client.post(url, data)
assert response.status_code == status.HTTP_201_CREATED
assert response.data['field1'] == 'value1'
@pytest.mark.django_db
def test_list_resources(self, authenticated_client):
client, user = authenticated_client
ResourceNameFactory.create_batch(3, created_by=user)
url = reverse('resource-list')
response = client.get(url)
assert response.status_code == status.HTTP_200_OK
assert len(response.data) == 3
Patterns & Best Practices
Error Response Format
Always use consistent error responses:
{
"error": "Human-readable message",
"code": "MACHINE_READABLE_CODE",
"details": {} # Optional additional context
}
Pagination
Use cursor pagination for large datasets:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 20,
}
Filtering
Use django-filter for query parameter filtering:
from django_filters import rest_framework as filters
class ResourceFilter(filters.FilterSet):
created_after = filters.DateTimeFilter(field_name='created_at', lookup_expr='gte')
class Meta:
model = ResourceName
fields = ['status', 'created_after']
Common Pitfalls
- N+1 Queries: Use
select_related()andprefetch_related()in querysets - Missing Permissions: Always add
permission_classesdecorator - No Validation: Add custom validation in serializer methods
- Inconsistent Responses: Use the same response format across all endpoints
Verification
Test the endpoints:
# Create resource
curl -X POST http://localhost:8000/api/resources/create/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"field1": "value1"}'
# List resources
curl http://localhost:8000/api/resources/ \
-H "Authorization: Bearer $TOKEN"