skills/django-clean-drf/SKILL.md
Create production-quality Django REST Framework APIs using Clean Architecture and SOLID principles. Covers layered architecture (views, use cases, services, models), query optimization (N+1 prevention), pagination/filtering, JWT authentication, permissions, and production deployment. Use when building new Django APIs, implementing domain-driven design, optimizing queries, or configuring authentication. Applies Python 3.12+ and Django 5+ patterns.
npx skillsauth add agusabas/django-skills django-clean-drfInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides expert guidance for creating Django REST Framework applications following Clean Architecture principles. It enforces a strict layered architecture that separates concerns, maximizes testability, and produces maintainable production-quality code.
Architecture Layers:
HTTP Layer (Views/Serializers)
↓
Application Layer (Use Cases + DTOs)
↓
Domain Layer (Services + Models)
↓
Infrastructure Layer (ORM, External APIs, Cache)
Key Capabilities:
Invoke this skill when you encounter these triggers:
New Application Setup:
Use Case Implementation:
Service Layer:
Architecture Questions:
API Design:
Follow this workflow when handling Django Clean DRF requests:
Understand the domain:
Check existing project structure:
Determine scope:
Based on the task, reference the appropriate bundled documentation:
Architecture:
references/project-structure.mdreferences/use-cases-pattern.mdreferences/services-pattern.mdreferences/models-domain.mdreferences/views-serializers.mdreferences/testing-clean-arch.mdreferences/examples.mdAPI Development:
references/query-optimization.mdreferences/api-patterns.mdreferences/authentication.mdreferences/production-api.mdreferences/django-admin.mdCode Quality:
references/coding-standards.mdCRITICAL: Layer Dependency Rules
Views → Use Cases → Services → Models
↓ ↓ ↓ ↓
Serializers DTOs (internal) ORM
Use Case Pattern:
from dataclasses import dataclass
from uuid import UUID
from django.db import transaction
@dataclass(frozen=True, slots=True)
class CreateOrderInput:
customer_id: UUID
items: list['OrderItemInput']
notes: str | None = None
@dataclass(frozen=True, slots=True)
class CreateOrderOutput:
success: bool
order_id: UUID | None = None
error: str | None = None
class CreateOrderUseCase:
def __init__(
self,
order_service: OrderService,
inventory_service: InventoryService,
):
self._order_service = order_service
self._inventory_service = inventory_service
@transaction.atomic
def execute(self, input_dto: CreateOrderInput) -> CreateOrderOutput:
# Validate
is_valid, error = self._inventory_service.validate_availability(input_dto.items)
if not is_valid:
return CreateOrderOutput(success=False, error=error)
# Execute
order = self._order_service.create(
customer_id=input_dto.customer_id,
items=input_dto.items,
)
return CreateOrderOutput(success=True, order_id=order.id)
Service Pattern:
class OrderService:
# STATIC methods for QUERIES (no state changes)
@staticmethod
def get_by_id(order_id: UUID) -> Order | None:
return Order.objects.select_related('customer').filter(id=order_id).first()
# INSTANCE methods for MUTATIONS (state changes)
def create(self, customer_id: UUID, items: list[OrderItemInput]) -> Order:
order = Order.objects.create(customer_id=customer_id)
for item in items:
OrderItem.objects.create(order=order, **item.__dict__)
return order
# VALIDATION returns tuple[bool, str]
def validate_can_cancel(self, order: Order) -> tuple[bool, str]:
if order.status == Order.Status.SHIPPED:
return False, "Cannot cancel shipped orders"
return True, ""
Thin View Pattern:
class CreateOrderView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request: Request) -> Response:
serializer = CreateOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
use_case = CreateOrderUseCase(
order_service=OrderService(),
inventory_service=InventoryService(),
)
input_dto = CreateOrderInput(**serializer.validated_data)
output = use_case.execute(input_dto)
if not output.success:
return Response({'error': output.error}, status=400)
return Response({'order_id': str(output.order_id)}, status=201)
Serializer Separation:
# WRITE serializer (input validation)
class CreateOrderSerializer(serializers.Serializer):
items = OrderItemInputSerializer(many=True, min_length=1)
notes = serializers.CharField(max_length=500, required=False)
# READ serializer (output display)
class OrderReadSerializer(serializers.ModelSerializer):
customer_name = serializers.CharField(source='customer.name', read_only=True)
class Meta:
model = Order
fields = ['id', 'customer_name', 'status', 'created_at']
read_only_fields = fields
Model with Domain Logic:
class Order(models.Model):
class Status(models.TextChoices):
PENDING = 'pending', 'Pending'
CONFIRMED = 'confirmed', 'Confirmed'
SHIPPED = 'shipped', 'Shipped'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
customer = models.ForeignKey('Customer', on_delete=models.PROTECT, related_name='orders')
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
indexes = [models.Index(fields=['customer', 'status'])]
@property
def is_cancellable(self) -> bool:
return self.status in (self.Status.PENDING, self.Status.CONFIRMED)
Architecture Checklist:
Code Quality Checklist:
tuple[bool, str]@transaction.atomic wraps state changesPerformance Checklist:
select_related for ForeignKey accessprefetch_related for reverse relationsapps/<app_name>/
├── __init__.py
├── models.py # Domain entities with @property logic
├── views.py # Thin HTTP handlers
├── serializers.py # Read and Write serializers
├── urls.py # Route definitions
├── admin.py # Django Admin
├── services/
│ ├── __init__.py # Export services
│ └── <entity>_service.py
├── use_cases/
│ ├── __init__.py # Export use cases
│ └── <action>_<entity>.py # One file per use case
├── tests/
│ ├── __init__.py
│ ├── test_models.py
│ ├── test_services.py
│ ├── test_use_cases.py
│ ├── test_views.py
│ └── factories.py # Factory Boy factories
└── migrations/
references/ - Comprehensive Clean Architecture and API documentation
references/project-structure.md
Directory layout, file naming, module organization, dependency rules
references/use-cases-pattern.md
Input/Output DTOs, constructor injection, transaction handling, error handling
references/services-pattern.md
Static vs instance methods, validation patterns, cross-service communication
references/models-domain.md
Domain logic in models, TextChoices, UUID keys, indexes
references/views-serializers.md
Thin views, permissions, Read vs Write serializers
references/testing-clean-arch.md
Unit testing use cases, mocking services, integration tests
references/examples.md
Complete CRUD example, complex workflows, testing examples
references/query-optimization.md
N+1 queries, select_related/prefetch_related, indexes, bulk operations, aggregations
references/api-patterns.md
Pagination, filtering, searching, ordering, API versioning, error handling, throttling
references/authentication.md
JWT authentication, permissions, API keys, role-based access, security headers
references/production-api.md
Settings structure, database config, caching, logging, Sentry, health checks, Docker
references/django-admin.md
ModelAdmin configuration, inlines, custom actions, query optimization, permissions, security
references/coding-standards.md
Ruff configuration, import ordering, security practices, YAGNI, logging, documentation# BAD
def create_order(self, data):
if not self.can_create():
raise ValidationError("Cannot create order")
return Order.objects.create(**data)
# GOOD
def create_order(self, data) -> CreateOrderOutput:
if not self.can_create():
return CreateOrderOutput(success=False, error="Cannot create order")
order = Order.objects.create(**data)
return CreateOrderOutput(success=True, order_id=order.id)
# BAD
def validate(self, order) -> bool:
return order.status != 'shipped'
# GOOD
def validate(self, order) -> tuple[bool, str]:
if order.status == 'shipped':
return False, "Cannot modify shipped orders"
return True, ""
# BAD - Hard to test
class CreateOrderUseCase:
def execute(self, input_dto):
service = OrderService() # Hardcoded dependency
return service.create(input_dto)
# GOOD - Easy to mock
class CreateOrderUseCase:
def __init__(self, order_service: OrderService):
self._order_service = order_service
def execute(self, input_dto):
return self._order_service.create(input_dto)
Python Version: 3.10+ required (for X | None union syntax), 3.12+ recommended
Django Version: 4.2 LTS minimum, 5.0+ recommended
Naming Conventions:
<action>_<entity>.py (e.g., create_order.py)<entity>_service.py (e.g., order_service.py)<Entity>ReadSerializer, <Entity>CreateSerializerIntegration with Other Skills:
django-celery-expert for background tasksrefactordjango to migrate existing code to this architecturedevelopment
Refactor Django/Python code to improve maintainability, readability, and adherence to best practices. Transforms fat views into Clean Architecture with Use Cases and Services. Applies SOLID principles, Clean Code patterns, Python 3.12+ features like type parameter syntax and @override decorator, Django 5+ patterns like GeneratedField and async views. Fixes N+1 queries, extracts business logic from views, separates Read/Write serializers, and converts exception-based error handling to explicit return values. Use when refactoring Django code, applying Clean Architecture, or modernizing legacy Django projects.
development
Generate a valid Google Merchant Center product feed (RSS 2.0 / XML) from any backend. Use this skill when the user needs to create or fix a Google Merchant feed, product XML feed, Google Shopping feed, or integrate products with Google Merchant Center. Covers all required and recommended fields, validation rules, common pitfalls, and backend-specific implementations for Django/Python, Node.js, and Next.js API routes.
development
Expert Django and Celery guidance for asynchronous task processing. Use when designing background tasks, configuring workers, handling retries and errors, optimizing task performance, implementing periodic tasks, or setting up production monitoring. Follows Celery best practices with Django integration patterns.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.