<< All versions
Skill v1.0.0
currentAutomated scan100/100deepelementlab/clawcode/django-patterns
──Details
PublishedSeptember 29, 2026 at 06:11 PM
Content Hashsha256:70fcf7f23d71f704...
Git SHAe07ea4260643
──Files
Files (1 file, 20.5 KB)
SKILL.md20.5 KBactive
SKILL.md · 735 lines · 20.5 KB
version: "1.0.0" name: django-patterns description: Django architecture patterns, REST API design with DRF, ORM best practices, caching, signals, middleware, and production-grade Django apps.
Django Development Patterns
Production-grade Django architecture patterns for scalable, maintainable applications.
When to Activate
- Building Django web applications
- Designing Django REST Framework APIs
- Working with Django ORM and models
- Setting up Django project structure
- Implementing caching, signals, middleware
Project Structure
Recommended Layout
myproject/├── config/│ ├── __init__.py│ ├── settings/│ │ ├── __init__.py│ │ ├── base.py # Base settings│ │ ├── development.py # Dev settings│ │ ├── production.py # Production settings│ │ └── test.py # Test settings│ ├── urls.py│ ├── wsgi.py│ └── asgi.py├── manage.py└── apps/├── __init__.py├── users/│ ├── __init__.py│ ├── models.py│ ├── views.py│ ├── serializers.py│ ├── urls.py│ ├── permissions.py│ ├── filters.py│ ├── services.py│ └── tests/└── products/└── ...
Split Settings Pattern
python
# config/settings/base.pyfrom pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parent.parentSECRET_KEY = env('DJANGO_SECRET_KEY')DEBUG = FalseALLOWED_HOSTS = []INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','rest_framework.authtoken','corsheaders',# Local apps'apps.users','apps.products',]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','whitenoise.middleware.WhiteNoiseMiddleware','django.contrib.sessions.middleware.SessionMiddleware','corsheaders.middleware.CorsMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',]ROOT_URLCONF = 'config.urls'WSGI_APPLICATION = 'config.wsgi.application'DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql','NAME': env('DB_NAME'),'USER': env('DB_USER'),'PASSWORD': env('DB_PASSWORD'),'HOST': env('DB_HOST'),'PORT': env('DB_PORT', default='5432'),}}# config/settings/development.pyfrom .base import *DEBUG = TrueALLOWED_HOSTS = ['localhost', '127.0.0.1']DATABASES['default']['NAME'] = 'myproject_dev'INSTALLED_APPS += ['debug_toolbar']MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'# config/settings/production.pyfrom .base import *DEBUG = FalseALLOWED_HOSTS = env.list('ALLOWED_HOSTS')SECURE_SSL_REDIRECT = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = TrueSECURE_HSTS_SECONDS = 31536000SECURE_HSTS_INCLUDE_SUBDOMAINS = TrueSECURE_HSTS_PRELOAD = True# LoggingLOGGING = {'version': 1,'disable_existing_loggers': False,'handlers': {'file': {'level': 'WARNING','class': 'logging.FileHandler','filename': '/var/log/django/django.log',},},'loggers': {'django': {'handlers': ['file'],'level': 'WARNING','propagate': True,},},}
Model Design Patterns
Model Best Practices
python
from django.db import modelsfrom django.contrib.auth.models import AbstractUserfrom django.core.validators import MinValueValidator, MaxValueValidatorclass User(AbstractUser):"""Custom user model extending AbstractUser."""email = models.EmailField(unique=True)phone = models.CharField(max_length=20, blank=True)birth_date = models.DateField(null=True, blank=True)USERNAME_FIELD = 'email'REQUIRED_FIELDS = ['username']class Meta:db_table = 'users'verbose_name = 'user'verbose_name_plural = 'users'ordering = ['-date_joined']def __str__(self):return self.emaildef get_full_name(self):return f"{self.first_name} {self.last_name}".strip()class Product(models.Model):"""Product model with proper field configuration."""name = models.CharField(max_length=200)slug = models.SlugField(unique=True, max_length=250)description = models.TextField(blank=True)price = models.DecimalField(max_digits=10,decimal_places=2,validators=[MinValueValidator(0)])stock = models.PositiveIntegerField(default=0)is_active = models.BooleanField(default=True)category = models.ForeignKey('Category',on_delete=models.CASCADE,related_name='products')tags = models.ManyToManyField('Tag', blank=True, related_name='products')created_at = models.DateTimeField(auto_now_add=True)updated_at = models.DateTimeField(auto_now=True)class Meta:db_table = 'products'ordering = ['-created_at']indexes = [models.Index(fields=['slug']),models.Index(fields=['-created_at']),models.Index(fields=['category', 'is_active']),]constraints = [models.CheckConstraint(check=models.Q(price__gte=0),name='price_non_negative')]def __str__(self):return self.namedef save(self, *args, **kwargs):if not self.slug:self.slug = slugify(self.name)super().save(*args, **kwargs)
QuerySet Best Practices
python
from django.db import modelsclass ProductQuerySet(models.QuerySet):"""Custom QuerySet for Product model."""def active(self):"""Return only active products."""return self.filter(is_active=True)def with_category(self):"""Select related category to avoid N+1 queries."""return self.select_related('category')def with_tags(self):"""Prefetch tags for many-to-many relationship."""return self.prefetch_related('tags')def in_stock(self):"""Return products with stock > 0."""return self.filter(stock__gt=0)def search(self, query):"""Search products by name or description."""return self.filter(models.Q(name__icontains=query) |models.Q(description__icontains=query))class Product(models.Model):# ... fields ...objects = ProductQuerySet.as_manager() # Use custom QuerySet# UsageProduct.objects.active().with_category().in_stock()
Manager Methods
python
class ProductManager(models.Manager):"""Custom manager for complex queries."""def get_or_none(self, **kwargs):"""Return object or None instead of DoesNotExist."""try:return self.get(**kwargs)except self.model.DoesNotExist:return Nonedef create_with_tags(self, name, price, tag_names):"""Create product with associated tags."""product = self.create(name=name, price=price)tags = [Tag.objects.get_or_create(name=name)[0] for name in tag_names]product.tags.set(tags)return productdef bulk_update_stock(self, product_ids, quantity):"""Bulk update stock for multiple products."""return self.filter(id__in=product_ids).update(stock=quantity)# In modelclass Product(models.Model):# ... fields ...custom = ProductManager()
Django REST Framework Patterns
Serializer Patterns
python
from rest_framework import serializersfrom django.contrib.auth.password_validation import validate_passwordfrom .models import Product, Userclass ProductSerializer(serializers.ModelSerializer):"""Serializer for Product model."""category_name = serializers.CharField(source='category.name', read_only=True)average_rating = serializers.FloatField(read_only=True)discount_price = serializers.SerializerMethodField()class Meta:model = Productfields = ['id', 'name', 'slug', 'description', 'price','discount_price', 'stock', 'category_name','average_rating', 'created_at']read_only_fields = ['id', 'slug', 'created_at']def get_discount_price(self, obj):"""Calculate discount price if applicable."""if hasattr(obj, 'discount') and obj.discount:return obj.price * (1 - obj.discount.percent / 100)return obj.pricedef validate_price(self, value):"""Ensure price is non-negative."""if value < 0:raise serializers.ValidationError("Price cannot be negative.")return valueclass ProductCreateSerializer(serializers.ModelSerializer):"""Serializer for creating products."""class Meta:model = Productfields = ['name', 'description', 'price', 'stock', 'category']def validate(self, data):"""Custom validation for multiple fields."""if data['price'] > 10000 and data['stock'] > 100:raise serializers.ValidationError("Cannot have high-value products with large stock.")return dataclass UserRegistrationSerializer(serializers.ModelSerializer):"""Serializer for user registration."""password = serializers.CharField(write_only=True,required=True,validators=[validate_password],style={'input_type': 'password'})password_confirm = serializers.CharField(write_only=True, style={'input_type': 'password'})class Meta:model = Userfields = ['email', 'username', 'password', 'password_confirm']def validate(self, data):"""Validate passwords match."""if data['password'] != data['password_confirm']:raise serializers.ValidationError({"password_confirm": "Password fields didn't match."})return datadef create(self, validated_data):"""Create user with hashed password."""validated_data.pop('password_confirm')password = validated_data.pop('password')user = User.objects.create(**validated_data)user.set_password(password)user.save()return user
ViewSet Patterns
python
from rest_framework import viewsets, status, filtersfrom rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework.permissions import IsAuthenticated, IsAdminUserfrom django_filters.rest_framework import DjangoFilterBackendfrom .models import Productfrom .serializers import ProductSerializer, ProductCreateSerializerfrom .permissions import IsOwnerOrReadOnlyfrom .filters import ProductFilterfrom .services import ProductServiceclass ProductViewSet(viewsets.ModelViewSet):"""ViewSet for Product model."""queryset = Product.objects.select_related('category').prefetch_related('tags')permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]filterset_class = ProductFiltersearch_fields = ['name', 'description']ordering_fields = ['price', 'created_at', 'name']ordering = ['-created_at']def get_serializer_class(self):"""Return appropriate serializer based on action."""if self.action == 'create':return ProductCreateSerializerreturn ProductSerializerdef perform_create(self, serializer):"""Save with user context."""serializer.save(created_by=self.request.user)@action(detail=False, methods=['get'])def featured(self, request):"""Return featured products."""featured = self.queryset.filter(is_featured=True)[:10]serializer = self.get_serializer(featured, many=True)return Response(serializer.data)@action(detail=True, methods=['post'])def purchase(self, request, pk=None):"""Purchase a product."""product = self.get_object()service = ProductService()result = service.purchase(product, request.user)return Response(result, status=status.HTTP_201_CREATED)@action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])def my_products(self, request):"""Return products created by current user."""products = self.queryset.filter(created_by=request.user)page = self.paginate_queryset(products)serializer = self.get_serializer(page, many=True)return self.get_paginated_response(serializer.data)
Custom Actions
python
from rest_framework.decorators import api_view, permission_classesfrom rest_framework.permissions import IsAuthenticatedfrom rest_framework.response import Response@api_view(['POST'])@permission_classes([IsAuthenticated])def add_to_cart(request):"""Add product to user cart."""product_id = request.data.get('product_id')quantity = request.data.get('quantity', 1)try:product = Product.objects.get(id=product_id)except Product.DoesNotExist:return Response({'error': 'Product not found'},status=status.HTTP_404_NOT_FOUND)cart, _ = Cart.objects.get_or_create(user=request.user)CartItem.objects.create(cart=cart,product=product,quantity=quantity)return Response({'message': 'Added to cart'}, status=status.HTTP_201_CREATED)
Service Layer Pattern
python
# apps/orders/services.pyfrom typing import Optionalfrom django.db import transactionfrom .models import Order, OrderItemclass OrderService:"""Service layer for order-related business logic."""@staticmethod@transaction.atomicdef create_order(user, cart: Cart) -> Order:"""Create order from cart."""order = Order.objects.create(user=user,total_price=cart.total_price)for item in cart.items.all():OrderItem.objects.create(order=order,product=item.product,quantity=item.quantity,price=item.product.price)# Clear cartcart.items.all().delete()return order@staticmethoddef process_payment(order: Order, payment_data: dict) -> bool:"""Process payment for order."""# Integration with payment gatewaypayment = PaymentGateway.charge(amount=order.total_price,token=payment_data['token'])if payment.success:order.status = Order.Status.PAIDorder.save()# Send confirmation emailOrderService.send_confirmation_email(order)return Truereturn False@staticmethoddef send_confirmation_email(order: Order):"""Send order confirmation email."""# Email sending logicpass
Caching Strategies
View-Level Caching
python
from django.views.decorators.cache import cache_pagefrom django.utils.decorators import method_decorator@method_decorator(cache_page(60 * 15), name='dispatch') # 15 minutesclass ProductListView(generic.ListView):model = Producttemplate_name = 'products/list.html'context_object_name = 'products'
Template Fragment Caching
django
{% load cache %}{% cache 500 sidebar %}... expensive sidebar content ...{% endcache %}
Low-Level Caching
python
from django.core.cache import cachedef get_featured_products():"""Get featured products with caching."""cache_key = 'featured_products'products = cache.get(cache_key)if products is None:products = list(Product.objects.filter(is_featured=True))cache.set(cache_key, products, timeout=60 * 15) # 15 minutesreturn products
QuerySet Caching
python
from django.core.cache import cachedef get_popular_categories():cache_key = 'popular_categories'categories = cache.get(cache_key)if categories is None:categories = list(Category.objects.annotate(product_count=Count('products')).filter(product_count__gt=10).order_by('-product_count')[:20])cache.set(cache_key, categories, timeout=60 * 60) # 1 hourreturn categories
Signals
Signal Patterns
python
# apps/users/signals.pyfrom django.db.models.signals import post_savefrom django.dispatch import receiverfrom django.contrib.auth import get_user_modelfrom .models import ProfileUser = get_user_model()@receiver(post_save, sender=User)def create_user_profile(sender, instance, created, **kwargs):"""Create profile when user is created."""if created:Profile.objects.create(user=instance)@receiver(post_save, sender=User)def save_user_profile(sender, instance, **kwargs):"""Save profile when user is saved."""instance.profile.save()# apps/users/apps.pyfrom django.apps import AppConfigclass UsersConfig(AppConfig):default_auto_field = 'django.db.models.BigAutoField'name = 'apps.users'def ready(self):"""Import signals when app is ready."""import apps.users.signals
Middleware
Custom Middleware
python
# middleware/active_user_middleware.pyimport timefrom django.utils.deprecation import MiddlewareMixinclass ActiveUserMiddleware(MiddlewareMixin):"""Middleware to track active users."""def process_request(self, request):"""Process incoming request."""if request.user.is_authenticated:# Update last active timerequest.user.last_active = timezone.now()request.user.save(update_fields=['last_active'])class RequestLoggingMiddleware(MiddlewareMixin):"""Middleware for logging requests."""def process_request(self, request):"""Log request start time."""request.start_time = time.time()def process_response(self, request, response):"""Log request duration."""if hasattr(request, 'start_time'):duration = time.time() - request.start_timelogger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3f}s')return response
Performance Optimization
N+1 Query Prevention
python
# Bad - N+1 queriesproducts = Product.objects.all()for product in products:print(product.category.name) # Separate query for each product# Good - Single query with select_relatedproducts = Product.objects.select_related('category').all()for product in products:print(product.category.name)# Good - Prefetch for many-to-manyproducts = Product.objects.prefetch_related('tags').all()for product in products:for tag in product.tags.all():print(tag.name)
Database Indexing
python
class Product(models.Model):name = models.CharField(max_length=200, db_index=True)slug = models.SlugField(unique=True)category = models.ForeignKey('Category', on_delete=models.CASCADE)created_at = models.DateTimeField(auto_now_add=True)class Meta:indexes = [models.Index(fields=['name']),models.Index(fields=['-created_at']),models.Index(fields=['category', 'created_at']),]
Bulk Operations
python
# Bulk createProduct.objects.bulk_create([Product(name=f'Product {i}', price=10.00)for i in range(1000)])# Bulk updateproducts = Product.objects.all()[:100]for product in products:product.is_active = TrueProduct.objects.bulk_update(products, ['is_active'])# Bulk deleteProduct.objects.filter(stock=0).delete()
Quick Reference
| Pattern | Description | |
|---|---|---|
| Split settings | Separate dev/prod/test settings | |
| Custom QuerySet | Reusable query methods | |
| Service Layer | Business logic separation | |
| ViewSet | REST API endpoints | |
| Serializer validation | Request/response transformation | |
| select_related | Foreign key optimization | |
| prefetch_related | Many-to-many optimization | |
| Cache first | Cache expensive operations | |
| Signals | Event-driven actions | |
| Middleware | Request/response processing |
Remember: Django provides many shortcuts, but for production applications, structure and organization matter more than concise code. Build for maintainability.