$npx -y skills add tranhieutt/software_development_department --skill django-patternsProvides expert-level Django development patterns covering App Router (indirectly via REST/GraphQL), async views, DRF, Celery, signals, middleware, and performance optimization. Use when building complex Django 5.x applications or identifying N+1 query issues.
| 1 | # Django & DRF Professional Patterns |
| 2 | |
| 3 | ## Core Expertise |
| 4 | - **Modern Django:** 5.x features, async views/middleware, ASGI deployment. |
| 5 | - **Background & Real-time:** Celery integration, Django Channels. |
| 6 | - **ORM Optimization:** select_related, prefetch_related, custom managers. |
| 7 | - **Security:** JWT auth, OAuth2, RBAC, protection against SQLi/XSS/CSRF. |
| 8 | |
| 9 | ## Critical rules (non-obvious) |
| 10 | |
| 11 | - **N+1 queries**: always use `select_related` (FK) / `prefetch_related` (M2M) — never iterate and query inside loops |
| 12 | - **`get_or_create` race condition**: wrap in `transaction.atomic()` in concurrent environments |
| 13 | - **Never call `save()` inside `pre_save` signal** — causes infinite recursion; use `update_fields` |
| 14 | - **`bulk_create` skips signals and `save()`** — don't use when signal logic is required |
| 15 | - **Migrations on large tables**: use `RunSQL` with `CONCURRENTLY` index creation to avoid locks |
| 16 | |
| 17 | ## ORM: select_related vs prefetch_related |
| 18 | |
| 19 | ```python |
| 20 | # FK / OneToOne → select_related (JOIN) |
| 21 | books = Book.objects.select_related("author", "author__publisher").all() |
| 22 | |
| 23 | # M2M / reverse FK → prefetch_related (separate query) |
| 24 | authors = Author.objects.prefetch_related("books", "books__tags").all() |
| 25 | |
| 26 | # Custom prefetch with queryset |
| 27 | from django.db.models import Prefetch |
| 28 | Author.objects.prefetch_related( |
| 29 | Prefetch("books", queryset=Book.objects.filter(published=True), to_attr="active_books") |
| 30 | ) |
| 31 | ``` |
| 32 | |
| 33 | ## ORM: annotations and aggregations |
| 34 | |
| 35 | ```python |
| 36 | from django.db.models import Count, Avg, Q, F, Value |
| 37 | from django.db.models.functions import Coalesce |
| 38 | |
| 39 | Author.objects.annotate( |
| 40 | book_count=Count("books"), |
| 41 | avg_rating=Coalesce(Avg("books__rating"), Value(0.0)), |
| 42 | high_rated=Count("books", filter=Q(books__rating__gte=4)), |
| 43 | ).filter(book_count__gt=0).order_by("-book_count") |
| 44 | ``` |
| 45 | |
| 46 | ## ORM: F expressions (avoid race conditions) |
| 47 | |
| 48 | ```python |
| 49 | # BAD — race condition |
| 50 | product = Product.objects.get(pk=pk) |
| 51 | product.stock -= quantity |
| 52 | product.save() |
| 53 | |
| 54 | # GOOD — atomic at DB level |
| 55 | Product.objects.filter(pk=pk).update(stock=F("stock") - quantity) |
| 56 | ``` |
| 57 | |
| 58 | ## Views: class-based view pattern |
| 59 | |
| 60 | ```python |
| 61 | from django.views import View |
| 62 | from django.contrib.auth.mixins import LoginRequiredMixin |
| 63 | |
| 64 | class OrderDetailView(LoginRequiredMixin, View): |
| 65 | def get(self, request, pk): |
| 66 | order = get_object_or_404(Order.objects.select_related("user"), pk=pk, user=request.user) |
| 67 | return JsonResponse(OrderSerializer(order).data) |
| 68 | ``` |
| 69 | |
| 70 | ## DRF: serializer with validation |
| 71 | |
| 72 | ```python |
| 73 | class ProductSerializer(serializers.ModelSerializer): |
| 74 | class Meta: |
| 75 | model = Product |
| 76 | fields = ["id", "name", "price", "stock"] |
| 77 | read_only_fields = ["id"] |
| 78 | |
| 79 | def validate_price(self, value): |
| 80 | if value <= 0: |
| 81 | raise serializers.ValidationError("Price must be positive.") |
| 82 | return value |
| 83 | |
| 84 | def validate(self, data): # cross-field |
| 85 | if data["stock"] == 0 and data.get("is_featured"): |
| 86 | raise serializers.ValidationError("Out-of-stock products cannot be featured.") |
| 87 | return data |
| 88 | ``` |
| 89 | |
| 90 | ## DRF: ViewSet with custom actions |
| 91 | |
| 92 | ```python |
| 93 | from rest_framework import viewsets, status |
| 94 | from rest_framework.decorators import action |
| 95 | from rest_framework.response import Response |
| 96 | |
| 97 | class ProductViewSet(viewsets.ModelViewSet): |
| 98 | queryset = Product.objects.select_related("category") |
| 99 | serializer_class = ProductSerializer |
| 100 | permission_classes = [IsAuthenticated] |
| 101 | |
| 102 | @action(detail=True, methods=["post"], url_path="archive") |
| 103 | def archive(self, request, pk=None): |
| 104 | product = self.get_object() |
| 105 | product.is_archived = True |
| 106 | product.save(update_fields=["is_archived"]) |
| 107 | return Response(status=status.HTTP_204_NO_CONTENT) |
| 108 | ``` |
| 109 | |
| 110 | ## DRF: filtering + pagination |
| 111 | |
| 112 | ```python |
| 113 | # settings.py |
| 114 | REST_FRAMEWORK = { |
| 115 | "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], |
| 116 | "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination", |
| 117 | "PAGE_SIZE": 20, |
| 118 | } |
| 119 | |
| 120 | # viewset |
| 121 | class ProductViewSet(viewsets.ReadOnlyModelViewSet): |
| 122 | filterset_fields = {"price": ["gte", "lte"], "category": ["exact"]} |
| 123 | ordering_fields = ["price", "created_at"] |
| 124 | search_fields = ["name", "description"] |
| 125 | ``` |
| 126 | |
| 127 | ## Celery task pattern |
| 128 | |
| 129 | ```python |
| 130 | from celery import shared_task |
| 131 | from django.db import transaction |
| 132 | |
| 133 | @shared_task(bind=True, max_retries=3, default_retry_delay=60) |
| 134 | def send_order_email(self |