- python-developer agent: Django/FastAPI backend specialist - nextjs-patterns skill: App Router, Server Components, Server Actions, Auth.js - vue-nuxt-patterns skill: Composition API, Pinia, Nitro server, SSR - react-patterns skill: hooks, Context, TanStack Query, React Hook Form - python-django-patterns skill: DRF, services, repositories - python-fastapi-patterns skill: async, Pydantic, SQLAlchemy, dependencies - /nextjs pipeline command for full-stack Next.js apps - /vue pipeline command for full-stack Vue/Nuxt apps - Updated frontend-developer with framework-specific skills - Updated orchestrator, capability-index for Python + frontend routing - Updated README, STRUCTURE, EVOLUTION_LOG with all new stacks Total agents: 30. Stacks: PHP, Next.js, Vue/Nuxt, React, Python, Go, Flutter, Node.js
243 lines
7.0 KiB
Markdown
243 lines
7.0 KiB
Markdown
---
|
|
name: python-django-patterns
|
|
description: Django 5+ patterns — models, views, serializers, REST framework, authentication, management commands
|
|
---
|
|
|
|
# Python Django Patterns
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
project/
|
|
├── manage.py
|
|
├── config/ # Project settings
|
|
│ ├── settings/
|
|
│ │ ├── base.py
|
|
│ │ ├── development.py
|
|
│ │ └── production.py
|
|
│ ├── urls.py
|
|
│ ├── wsgi.py
|
|
│ └── asgi.py
|
|
├── apps/
|
|
│ ├── products/
|
|
│ │ ├── models.py
|
|
│ │ ├── views.py # API views (thin)
|
|
│ │ ├── serializers.py # DRF serializers
|
|
│ │ ├── urls.py
|
|
│ │ ├── services.py # Business logic
|
|
│ │ ├── repositories.py # Data access
|
|
│ │ ├── admin.py
|
|
│ │ ├── tasks.py # Celery tasks
|
|
│ │ ├── tests/
|
|
│ │ │ ├── test_models.py
|
|
│ │ │ ├── test_views.py
|
|
│ │ │ └── test_services.py
|
|
│ │ └── migrations/
|
|
│ ├── orders/
|
|
│ └── users/
|
|
├── shared/
|
|
│ ├── pagination.py
|
|
│ ├── permissions.py
|
|
│ ├── throttling.py
|
|
│ └── exceptions.py
|
|
├── requirements/
|
|
│ ├── base.txt
|
|
│ ├── development.txt
|
|
│ └── production.txt
|
|
└── docker-compose.yml
|
|
```
|
|
|
|
## Model Pattern
|
|
|
|
```python
|
|
# apps/products/models.py
|
|
from django.db import models
|
|
from django.urls import reverse
|
|
|
|
|
|
class Product(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
slug = models.SlugField(unique=True)
|
|
description = models.TextField(blank=True)
|
|
price = models.DecimalField(max_digits=10, decimal_places=2)
|
|
category = models.ForeignKey('Category', on_delete=models.PROTECT, related_name='products')
|
|
is_active = models.BooleanField(default=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
indexes = [
|
|
models.Index(fields=['slug']),
|
|
models.Index(fields=['category', 'is_active']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def get_absolute_url(self):
|
|
return reverse('product-detail', kwargs={'slug': self.slug})
|
|
```
|
|
|
|
## Service Pattern (Business Logic in services.py)
|
|
|
|
```python
|
|
# apps/products/services.py
|
|
from .repositories import ProductRepository
|
|
|
|
|
|
class ProductService:
|
|
def __init__(self, repository=None):
|
|
self.repository = repository or ProductRepository()
|
|
|
|
def list_active(self, page=1, per_page=20, category_id=None, search=None):
|
|
return self.repository.list_active(
|
|
page=page, per_page=per_page,
|
|
category_id=category_id, search=search,
|
|
)
|
|
|
|
def create(self, data: dict):
|
|
product = self.repository.create(data)
|
|
return product
|
|
|
|
def update_price(self, product_id: int, new_price):
|
|
product = self.repository.get_by_id(product_id)
|
|
product.price = new_price
|
|
self.repository.save(product)
|
|
return product
|
|
```
|
|
|
|
## Repository Pattern
|
|
|
|
```python
|
|
# apps/products/repositories.py
|
|
from .models import Product
|
|
|
|
|
|
class ProductRepository:
|
|
def list_active(self, page=1, per_page=20, category_id=None, search=None):
|
|
qs = Product.objects.filter(is_active=True).select_related('category')
|
|
if category_id:
|
|
qs = qs.filter(category_id=category_id)
|
|
if search:
|
|
qs = qs.filter(name__icontains=search)
|
|
return qs[(page - 1) * per_page : page * per_page]
|
|
|
|
def get_by_id(self, product_id: int):
|
|
return Product.objects.select_related('category').get(pk=product_id)
|
|
|
|
def create(self, data: dict):
|
|
return Product.objects.create(**data)
|
|
|
|
def save(self, product):
|
|
product.save()
|
|
return product
|
|
```
|
|
|
|
## Serializer Pattern (DRF)
|
|
|
|
```python
|
|
# apps/products/serializers.py
|
|
from rest_framework import serializers
|
|
from .models import Product, Category
|
|
|
|
|
|
class CategorySerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = Category
|
|
fields = ['id', 'name', 'slug']
|
|
|
|
|
|
class ProductListSerializer(serializers.ModelSerializer):
|
|
category = CategorySerializer(read_only=True)
|
|
|
|
class Meta:
|
|
model = Product
|
|
fields = ['id', 'name', 'slug', 'price', 'category', 'is_active']
|
|
|
|
|
|
class ProductDetailSerializer(serializers.ModelSerializer):
|
|
category = CategorySerializer(read_only=True)
|
|
|
|
class Meta:
|
|
model = Product
|
|
fields = '__all__'
|
|
|
|
|
|
class ProductCreateSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = Product
|
|
fields = ['name', 'description', 'price', 'category', 'is_active']
|
|
|
|
def validate_price(self, value):
|
|
if value <= 0:
|
|
raise serializers.ValidationError('Price must be positive')
|
|
return value
|
|
```
|
|
|
|
## View Pattern (Thin Views)
|
|
|
|
```python
|
|
# apps/products/views.py
|
|
from rest_framework import viewsets, filters, status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from .models import Product
|
|
from .serializers import ProductListSerializer, ProductDetailSerializer, ProductCreateSerializer
|
|
from .services import ProductService
|
|
from .repositories import ProductRepository
|
|
|
|
|
|
class ProductViewSet(viewsets.ModelViewSet):
|
|
queryset = Product.objects.filter(is_active=True).select_related('category')
|
|
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
|
|
search_fields = ['name', 'description']
|
|
ordering_fields = ['price', 'created_at']
|
|
|
|
def get_serializer_class(self):
|
|
if self.action == 'list':
|
|
return ProductListSerializer
|
|
if self.action in ('create', 'update', 'partial_update'):
|
|
return ProductCreateSerializer
|
|
return ProductDetailSerializer
|
|
|
|
def get_service(self):
|
|
return ProductService(ProductRepository())
|
|
|
|
def create(self, request):
|
|
service = self.get_service()
|
|
product = service.create(request.data)
|
|
return Response(
|
|
ProductDetailSerializer(product).data,
|
|
status=status.HTTP_201_CREATED,
|
|
)
|
|
```
|
|
|
|
## Authentication (DRF + JWT)
|
|
|
|
```python
|
|
# config/settings/base.py
|
|
REST_FRAMEWORK = {
|
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
|
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
|
],
|
|
'DEFAULT_PERMISSION_CLASSES': [
|
|
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
|
|
],
|
|
'DEFAULT_PAGINATION_CLASS': 'shared.pagination.StandardPagination',
|
|
'PAGE_SIZE': 20,
|
|
}
|
|
```
|
|
|
|
## Checklist
|
|
|
|
- [ ] Fat models, thin views, services for business logic
|
|
- [ ] Repository pattern for data access
|
|
- [ ] DRF serializers for input validation and output transformation
|
|
- [ ] Select_related / prefetch_related to prevent N+1
|
|
- [ ] JWT (SimpleJWT) for API authentication
|
|
- [ ] Environment-specific settings (base/development/production)
|
|
- [ ] Django admin for data management
|
|
- [ ] pytest-django for testing
|
|
- [ ] Celery for async tasks
|
|
- [ ] `python manage.py check --deploy` before release |