mirror of
https://github.com/duthaho/claudekit.git
synced 2026-07-16 12:45:17 +03:00
6.2 KiB
6.2 KiB
Django Patterns Quick Reference
QuerySet Patterns
select_related (FK/OneToOne - single JOIN)
# BAD: N+1 queries
for order in Order.objects.all():
print(order.customer.name) # Hits DB each iteration
# GOOD: 1 query with JOIN
for order in Order.objects.select_related("customer"):
print(order.customer.name)
# Chain through multiple FKs
Order.objects.select_related("customer__company")
prefetch_related (M2M/reverse FK - separate query)
# BAD: N+1 on reverse FK
for author in Author.objects.all():
print(author.book_set.all()) # Query per author
# GOOD: 2 queries total
for author in Author.objects.prefetch_related("books"):
print(author.books.all())
# Custom prefetch with filtering
from django.db.models import Prefetch
Author.objects.prefetch_related(
Prefetch("books", queryset=Book.objects.filter(published=True), to_attr="published_books")
)
F Objects (reference model fields in queries)
from django.db.models import F
Product.objects.filter(stock__lt=F("reorder_level")) # Compare fields
Product.objects.filter(id=1).update(stock=F("stock") - 1) # Atomic update
Order.objects.filter(amount__gt=F("customer__credit_limit")) # Across relations
Q Objects (complex lookups with OR/NOT)
from django.db.models import Q
# OR
User.objects.filter(Q(role="admin") | Q(is_superuser=True))
# NOT
User.objects.filter(~Q(status="banned"))
# Complex combinations
User.objects.filter(
(Q(role="admin") | Q(role="staff")) & ~Q(status="inactive")
)
# Dynamic query building
conditions = Q()
if name: conditions &= Q(name__icontains=name)
if email: conditions &= Q(email__icontains=email)
User.objects.filter(conditions)
Subquery and OuterRef
from django.db.models import Subquery, OuterRef, Exists
# Subquery: latest order date per customer
latest_order = Order.objects.filter(
customer=OuterRef("pk")
).order_by("-created_at").values("created_at")[:1]
Customer.objects.annotate(last_order=Subquery(latest_order))
# Exists: customers with orders
Customer.objects.annotate(
has_orders=Exists(Order.objects.filter(customer=OuterRef("pk")))
).filter(has_orders=True)
Aggregation
from django.db.models import Count, Sum, Avg
# Aggregate (returns dict)
Order.objects.aggregate(total=Sum("amount"), avg=Avg("amount"))
# Annotate (per-row)
Customer.objects.annotate(order_count=Count("orders"))
Model Patterns
Abstract Base Model
class TimestampMixin(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # No DB table created
class Order(TimestampMixin):
amount = models.DecimalField(max_digits=10, decimal_places=2)
# Inherits created_at, updated_at
Proxy Model (same table, different behavior)
class PendingOrderManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status="pending")
class PendingOrder(Order):
objects = PendingOrderManager()
class Meta:
proxy = True # Same DB table as Order
Custom Manager and QuerySet
class PublishedQuerySet(models.QuerySet):
def published(self):
return self.filter(status="published")
def by_author(self, author):
return self.filter(author=author)
class Article(models.Model):
objects = PublishedQuerySet.as_manager()
# Chainable: Article.objects.published().by_author(user)
View Patterns
Class-Based View Mixins
| Mixin | Purpose |
|---|---|
LoginRequiredMixin |
Require authentication |
PermissionRequiredMixin |
Require specific permission |
UserPassesTestMixin |
Custom permission test |
FormView |
Handle form display + submission |
CreateView / UpdateView |
Model form CRUD |
ListView / DetailView |
Read operations |
class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = Order
permission_required = "orders.view_order"
paginate_by = 25
def get_queryset(self):
return super().get_queryset().filter(customer=self.request.user)
Django REST Framework Patterns
Nested Serializers
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = ["street", "city", "zip_code"]
class CustomerSerializer(serializers.ModelSerializer):
address = AddressSerializer()
class Meta:
model = Customer
fields = ["id", "name", "address"]
def create(self, validated_data):
address_data = validated_data.pop("address")
address = Address.objects.create(**address_data)
return Customer.objects.create(address=address, **validated_data)
Custom Permissions
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.owner == request.user
class IsAdminOrReadOnly(BasePermission):
def has_permission(self, request, view):
if request.method in ("GET", "HEAD", "OPTIONS"):
return True
return request.user.is_staff
# Usage
class OrderViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, IsOwner]
ViewSet Actions
from rest_framework.decorators import action
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
@action(detail=True, methods=["post"])
def cancel(self, request, pk=None):
order = self.get_object()
order.cancel()
return Response({"status": "cancelled"})
@action(detail=False, methods=["get"])
def summary(self, request):
return Response(self.get_queryset().aggregate(total=Sum("amount"), count=Count("id")))
Filtering (django-filter)
class OrderFilter(django_filters.FilterSet):
min_amount = django_filters.NumberFilter(field_name="amount", lookup_expr="gte")
max_amount = django_filters.NumberFilter(field_name="amount", lookup_expr="lte")
class Meta:
model = Order
fields = ["status", "customer"]