2026-02-04 15:29:11 +01:00

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() and prefetch_related() in querysets
  • Missing Permissions: Always add permission_classes decorator
  • 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"