Skip to main content
django-patterns Django architecture patterns: REST API design with DRF, ORM best practices, caching, signals, middleware, and production-grade app structure. USE WHEN structuring a Django app, building a DRF API, or optimizing ORM queries, caching, signals, or middleware.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Sheshiyer/skill-clusters --skill django-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name django-patterns description Django architecture patterns: REST API design with DRF, ORM best practices, caching, signals, middleware, and production-grade app structure. USE WHEN structuring a Django app, building a DRF API, or optimizing ORM queries, caching, signals, or middleware. origin ECC cluster python-backend version 1.0.0
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
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
SECRET_KEY = env('DJANGO_SECRET_KEY' )
DEBUG = False
ALLOWED_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' ,
'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' ),
}
}
from .base import *
DEBUG = True
ALLOWED_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'
from .base import *
DEBUG = False
ALLOWED_HOSTS = env.list ('ALLOWED_HOSTS' )
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
LOGGING = {
'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 from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator, MaxValueValidator
class 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 .email
def 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 .name
def save (self, *args, **kwargs ):
if not self .slug:
self .slug = slugify(self .name)
super ().save(*args, **kwargs)
QuerySet Best Practices from django.db import models
class 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):
objects = ProductQuerySet.as_manager()
Product.objects.active().with_category().in_stock()
Manager Methods 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 None
def 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 product
def bulk_update_stock (self, product_ids, quantity ):
"""Bulk update stock for multiple products."""
return self .filter (id__in=product_ids).update(stock=quantity)
class Product (models.Model):
custom = ProductManager()
Django REST Framework Patterns
Serializer Patterns from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import Product, User
class 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 = Product
fields = [
'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.price
def validate_price (self, value ):
"""Ensure price is non-negative."""
if value < 0 :
raise serializers.ValidationError("Price cannot be negative." )
return value
class ProductCreateSerializer (serializers.ModelSerializer):
"""Serializer for creating products."""
class Meta :
model = Product
fields = ['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 data
class 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 = User
fields = ['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 data
def 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 from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer, ProductCreateSerializer
from .permissions import IsOwnerOrReadOnly
from .filters import ProductFilter
from .services import ProductService
class 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 = ProductFilter
search_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 ProductCreateSerializer
return ProductSerializer
def 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 from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from 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
from typing import Optional
from django.db import transaction
from .models import Order, OrderItem
class OrderService :
"""Service layer for order-related business logic."""
@staticmethod
@transaction.atomic
def 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
)
cart.items.all ().delete()
return order
@staticmethod
def process_payment (order: Order, payment_data: dict ) -> bool :
"""Process payment for order."""
payment = PaymentGateway.charge(
amount=order.total_price,
token=payment_data['token' ]
)
if payment.success:
order.status = Order.Status.PAID
order.save()
OrderService.send_confirmation_email(order)
return True
return False
@staticmethod
def send_confirmation_email (order: Order ):
"""Send order confirmation email."""
pass
Caching Strategies
View-Level Caching from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
@method_decorator(cache_page(60 * 15 ), name='dispatch' )
class ProductListView (generic.ListView):
model = Product
template_name = 'products/list.html'
context_object_name = 'products'
Template Fragment Caching {% load cache %}
{% cache 500 sidebar %}
... expensive sidebar content ...
{% endcache %}
Low-Level Caching from django.core.cache import cache
def 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 )
return products
QuerySet Caching from django.core.cache import cache
def 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 )
return categories
Signals
Signal Patterns
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Profile
User = 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()
from django.apps import AppConfig
class 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
import time
from django.utils.deprecation import MiddlewareMixin
class ActiveUserMiddleware (MiddlewareMixin ):
"""Middleware to track active users."""
def process_request (self, request ):
"""Process incoming request."""
if request.user.is_authenticated:
request.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_time
logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.3 f} s' )
return response
Performance Optimization
N+1 Query Prevention
products = Product.objects.all ()
for product in products:
print (product.category.name)
products = Product.objects.select_related('category' ).all ()
for product in products:
print (product.category.name)
products = Product.objects.prefetch_related('tags' ).all ()
for product in products:
for tag in product.tags.all ():
print (tag.name)
Database Indexing 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
Product.objects.bulk_create([
Product(name=f'Product {i} ' , price=10.00 )
for i in range (1000 )
])
products = Product.objects.all ()[:100 ]
for product in products:
product.is_active = True
Product.objects.bulk_update(products, ['is_active' ])
Product.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.
이 저장소의 다른 Skills Arcplume runs Grok through the Grok Build CLI's own OAuth-authenticated session (grok login) for image generation, with strict preflight validation, secret-safe handling, and headless CLI-driven execution -- no separate XAI_API_KEY billing. Video falls back to the billed xAI API. USE WHEN a user wants to generate an image via a locally logged-in Grok Build CLI session, e.g. 'generate an image with grok', 'use grok build', or 'use my logged-in grok session'.
Shared reference for the Selemene cluster: the two report surfaces (deterministic Rust reports vs. narrative witness-pipeline readings), the @selemene/bridge CLI contract, the output manifest format, and non-prescriptive witnessing tone. USE WHEN deciding which Selemene surface to invoke or when routing between birth/compatibility/transit reports and solo/dyadic readings.
Route Selemene Engine tasks to the right surface: deterministic reports (birth/compatibility/transit) via selemene-core and the @selemene/bridge CLI, or narrative witness readings via selemene-report. USE WHEN the user wants anything under the Selemene/Noesis umbrella but has not named the exact surface.