mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-17 23:28:04 +02:00
* fix(skills): move version into metadata and normalize to semver 29 skills declared `version` at the top level of their frontmatter. The schema reads it from `metadata`, so tooling that follows the schema either misses it or has to special-case the top level. Three motion skills also declared `version: 1.0`, which is not a valid semantic version; normalized to `1.0.0`. No behavioral change — frontmatter metadata only. * fix(skills): state activation triggers in skill descriptions 148 skills described what they cover but never named the situation that should trigger them. Since the description is what Claude matches against to decide whether to load a skill, a description without a trigger makes activation guesswork — the skill is either missed or loaded at the wrong time. Added a "Use when ..." clause to each, derived from the skill's own body (most already stated the trigger under "## When to Use" or in the opening line; that intent is now reflected in the frontmatter where it is actually read from). Descriptions were only appended to; no existing wording was removed. * fix(skills): sync activation triggers into the Codex skill mirror 10 of the skills whose descriptions changed are also mirrored under `.agents/skills/`, where the description was previously a verbatim copy. Left alone, the two surfaces would disagree about when the skill applies. Only the description line is synced; the Codex copies keep their reduced frontmatter, since that validator accepts only name, description, metadata, license, and allowed-tools. * fix(skills): correct three activation clauses from review - autonomous-loops: the clause pulled new loop work into a skill that its own body marks as a compatibility shim retained for one release. It now points at the canonical continuous-agent-loop instead. - continuous-learning: the description carried the v1 routing directive twice; collapsed to one. - homelab-pihole-dns: the clause fired on any broken home DNS. Narrowed to tasks that actually involve Pi-hole. * chore: retain current main lockfile --------- Co-authored-by: Çağrı Solakoğlu <cagri.solakoglu@vtcenerji.com> Co-authored-by: haelyra <49814733+haelyra@users.noreply.github.com>
736 lines
21 KiB
Markdown
736 lines
21 KiB
Markdown
---
|
|
name: django-patterns
|
|
description: Django architecture patterns, REST API design with DRF, ORM best practices, caching, signals, middleware, and production-grade Django apps. Use when building or reviewing Django apps, DRF APIs, ORM queries, or caching.
|
|
metadata:
|
|
origin: ECC
|
|
---
|
|
|
|
# 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.py
|
|
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',
|
|
# 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.py
|
|
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'
|
|
|
|
# config/settings/production.py
|
|
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
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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):
|
|
# ... fields ...
|
|
|
|
objects = ProductQuerySet.as_manager() # Use custom QuerySet
|
|
|
|
# Usage
|
|
Product.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 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)
|
|
|
|
# In model
|
|
class Product(models.Model):
|
|
# ... fields ...
|
|
custom = ProductManager()
|
|
```
|
|
|
|
## Django REST Framework Patterns
|
|
|
|
### Serializer Patterns
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
# apps/orders/services.py
|
|
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
|
|
)
|
|
|
|
# Clear cart
|
|
cart.items.all().delete()
|
|
|
|
return order
|
|
|
|
@staticmethod
|
|
def process_payment(order: Order, payment_data: dict) -> bool:
|
|
"""Process payment for order."""
|
|
# Integration with payment gateway
|
|
payment = PaymentGateway.charge(
|
|
amount=order.total_price,
|
|
token=payment_data['token']
|
|
)
|
|
|
|
if payment.success:
|
|
order.status = Order.Status.PAID
|
|
order.save()
|
|
# Send confirmation email
|
|
OrderService.send_confirmation_email(order)
|
|
return True
|
|
|
|
return False
|
|
|
|
@staticmethod
|
|
def send_confirmation_email(order: Order):
|
|
"""Send order confirmation email."""
|
|
# Email sending logic
|
|
pass
|
|
```
|
|
|
|
## Caching Strategies
|
|
|
|
### View-Level Caching
|
|
|
|
```python
|
|
from django.views.decorators.cache import cache_page
|
|
from django.utils.decorators import method_decorator
|
|
|
|
@method_decorator(cache_page(60 * 15), name='dispatch') # 15 minutes
|
|
class ProductListView(generic.ListView):
|
|
model = Product
|
|
template_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 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) # 15 minutes
|
|
|
|
return products
|
|
```
|
|
|
|
### QuerySet Caching
|
|
|
|
```python
|
|
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) # 1 hour
|
|
|
|
return categories
|
|
```
|
|
|
|
## Signals
|
|
|
|
### Signal Patterns
|
|
|
|
```python
|
|
# apps/users/signals.py
|
|
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()
|
|
|
|
# apps/users/apps.py
|
|
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
|
|
|
|
```python
|
|
# middleware/active_user_middleware.py
|
|
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:
|
|
# Update last active time
|
|
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:.3f}s')
|
|
return response
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### N+1 Query Prevention
|
|
|
|
```python
|
|
# Bad - N+1 queries
|
|
products = Product.objects.all()
|
|
for product in products:
|
|
print(product.category.name) # Separate query for each product
|
|
|
|
# Good - Single query with select_related
|
|
products = Product.objects.select_related('category').all()
|
|
for product in products:
|
|
print(product.category.name)
|
|
|
|
# Good - Prefetch for many-to-many
|
|
products = 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 create
|
|
Product.objects.bulk_create([
|
|
Product(name=f'Product {i}', price=10.00)
|
|
for i in range(1000)
|
|
])
|
|
|
|
# Bulk update
|
|
products = Product.objects.all()[:100]
|
|
for product in products:
|
|
product.is_active = True
|
|
Product.objects.bulk_update(products, ['is_active'])
|
|
|
|
# Bulk delete
|
|
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.
|