home / skills / gentleman-programming / gentleman-skills / django-drf

django-drf skill

/curated/django-drf

This skill helps you implement Django REST Framework patterns with ViewSets, serializers, filters, and permissions to build robust APIs.

npx playbooks add skill gentleman-programming/gentleman-skills --skill django-drf

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.5 KB
---
name: django-drf
description: >
  Django REST Framework patterns.
  Trigger: When building REST APIs with Django - ViewSets, Serializers, Filters.
license: Apache-2.0
metadata:
  author: gentleman-programming
  version: "1.0"
---

## ViewSet Pattern

```python
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filterset_class = UserFilter
    permission_classes = [IsAuthenticated]

    def get_serializer_class(self):
        if self.action == "create":
            return UserCreateSerializer
        if self.action in ["update", "partial_update"]:
            return UserUpdateSerializer
        return UserSerializer

    @action(detail=True, methods=["post"])
    def activate(self, request, pk=None):
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({"status": "activated"})
```

## Serializer Patterns

```python
from rest_framework import serializers

# Read Serializer
class UserSerializer(serializers.ModelSerializer):
    full_name = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ["id", "email", "full_name", "created_at"]
        read_only_fields = ["id", "created_at"]

    def get_full_name(self, obj):
        return f"{obj.first_name} {obj.last_name}"

# Create Serializer
class UserCreateSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ["email", "password", "first_name", "last_name"]

    def create(self, validated_data):
        password = validated_data.pop("password")
        user = User(**validated_data)
        user.set_password(password)
        user.save()
        return user

# Update Serializer
class UserUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["first_name", "last_name"]
```

## Filters

```python
from django_filters import rest_framework as filters

class UserFilter(filters.FilterSet):
    email = filters.CharFilter(lookup_expr="icontains")
    is_active = filters.BooleanFilter()
    created_after = filters.DateTimeFilter(
        field_name="created_at",
        lookup_expr="gte"
    )
    created_before = filters.DateTimeFilter(
        field_name="created_at",
        lookup_expr="lte"
    )

    class Meta:
        model = User
        fields = ["email", "is_active"]
```

## Permissions

```python
from rest_framework.permissions import BasePermission

class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.owner == request.user

class IsAdminOrReadOnly(BasePermission):
    def has_permission(self, request, view):
        if request.method in ["GET", "HEAD", "OPTIONS"]:
            return True
        return request.user.is_staff
```

## Pagination

```python
from rest_framework.pagination import PageNumberPagination

class StandardPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = "page_size"
    max_page_size = 100

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "api.pagination.StandardPagination",
}
```

## URL Routing

```python
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"users", UserViewSet, basename="user")
router.register(r"posts", PostViewSet, basename="post")

urlpatterns = [
    path("api/v1/", include(router.urls)),
]
```

## Testing

```python
import pytest
from rest_framework import status
from rest_framework.test import APIClient

@pytest.fixture
def api_client():
    return APIClient()

@pytest.fixture
def authenticated_client(api_client, user):
    api_client.force_authenticate(user=user)
    return api_client

@pytest.mark.django_db
class TestUserViewSet:
    def test_list_users(self, authenticated_client):
        response = authenticated_client.get("/api/v1/users/")
        assert response.status_code == status.HTTP_200_OK

    def test_create_user(self, authenticated_client):
        data = {"email": "[email protected]", "password": "pass123"}
        response = authenticated_client.post("/api/v1/users/", data)
        assert response.status_code == status.HTTP_201_CREATED
```

## Commands

```bash
python manage.py runserver
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py shell
```

## Keywords
django, drf, rest framework, viewset, serializer, api, rest api

Overview

This skill provides concise, battle-tested Django REST Framework patterns for building REST APIs with ViewSets, Serializers, Filters, Permissions, Pagination, routing, and tests. It focuses on pragmatic defaults and clear separation of read/create/update serializers and reusable components to speed development and reduce bugs.

How this skill works

The skill supplies example ViewSet patterns with action-based serializer selection and custom actions, serializer templates for read/create/update use cases, and filter definitions using django-filter. It also includes permission classes, pagination configuration, router-based URL registration, and pytest examples for API tests so you can plug patterns into a project quickly.

When to use it

  • When building CRUD APIs with Django and DRF using ViewSets and routers
  • When you need separate serializers for read, create, and update operations
  • When you want consistent filtering, pagination, and permission behavior across endpoints
  • When writing automated API tests with pytest and DRF test client
  • When adding custom actions (e.g., activate) to resources

Best practices

  • Use ModelViewSet with get_serializer_class to return action-specific serializers
  • Keep read-only fields in read serializers and write-only fields (like password) in create serializers
  • Centralize filters in FilterSet classes and expose only safe fields
  • Write small, focused permission classes (owner/admin patterns) and attach them to viewsets
  • Configure a standard pagination class in settings to keep responses consistent
  • Cover key endpoints with pytest tests using APIClient and force_authenticate

Example use cases

  • User management endpoints with activation action, separate create/update serializers, and name formatting
  • Resource lists that support email contains, active flag, and created_at range filters
  • Admin vs read-only access control for collections and owner-restricted object access
  • Paginated list views with page_size query control and sane defaults
  • Automated tests for list and create operations to prevent regressions

FAQ

How do I handle passwords securely in serializers?

Expose password as write-only in the create serializer, call set_password in create(), and never return it in read serializers.

When should I split serializers by action?

Split when read and write shapes differ (computed fields, read-only metadata, or write-only sensitive fields) to keep validation and output explicit.