
Django Perf Review
Audit a Django codebase for provable ORM and database performance issues before scale bites you.
Install
npx skills add https://github.com/sickn33/antigravity-awesome-skills --skill django-perf-reviewWhat is this skill?
- Research-first review that reports only validated N+1, unbounded querysets, and index gaps
- Severity tiers aligned to impact (CRITICAL/HIGH focus on scale-breaking database behavior)
- Explicit rule: zero findings is acceptable—no speculative optimization filler
- Traces data flow with Read, Grep, Glob, Bash before filing findings
- Downgrades or skips findings when impact is minor despite scary patterns
Adoption & trust: 1 installs on skills.sh; 40.1k GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Improve Codebase Architecturemattpocock/skills
Zoom Outmattpocock/skills
Caveman Reviewjuliusbrussee/caveman
Requesting Code Reviewobra/superpowers
Receiving Code Reviewobra/superpowers
Request Refactor Planmattpocock/skills
Journey fit
Common Questions / FAQ
Is Django Perf Review safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Django Perf Review
# Django Performance Review Review Django code for **validated** performance issues. Research the codebase to confirm issues before reporting. Report only what you can prove. ## When to Use - You need a Django performance review focused on verified ORM and query issues. - The code likely has N+1 queries, unbounded querysets, missing indexes, or other database-driven bottlenecks. - You want only provable performance findings, not speculative optimization advice. ## Review Approach 1. **Research first** - Trace data flow, check for existing optimizations, verify data volume 2. **Validate before reporting** - Pattern matching is not validation 3. **Zero findings is acceptable** - Don't manufacture issues to appear thorough 4. **Severity must match impact** - If you catch yourself writing "minor" in a CRITICAL finding, it's not critical. Downgrade or skip it. ## Impact Categories Issues are organized by impact. Focus on CRITICAL and HIGH - these cause real problems at scale. | Priority | Category | Impact | |----------|----------|--------| | 1 | N+1 Queries | **CRITICAL** - Multiplies with data, causes timeouts | | 2 | Unbounded Querysets | **CRITICAL** - Memory exhaustion, OOM kills | | 3 | Missing Indexes | **HIGH** - Full table scans on large tables | | 4 | Write Loops | **HIGH** - Lock contention, slow requests | | 5 | Inefficient Patterns | **LOW** - Rarely worth reporting | --- ## Priority 1: N+1 Queries (CRITICAL) **Impact:** Each N+1 adds `O(n)` database round trips. 100 rows = 100 extra queries. 10,000 rows = timeout. ### Rule: Prefetch related data accessed in loops Validate by tracing: View → Queryset → Template/Serializer → Loop access ```python # PROBLEM: N+1 - each iteration queries profile def user_list(request): users = User.objects.all() return render(request, 'users.html', {'users': users}) # Template: # {% for user in users %} # {{ user.profile.bio }} ← triggers query per user # {% endfor %} # SOLUTION: Prefetch in view def user_list(request): users = User.objects.select_related('profile') return render(request, 'users.html', {'users': users}) ``` ### Rule: Prefetch in serializers, not just views DRF serializers accessing related fields cause N+1 if queryset isn't optimized. ```python # PROBLEM: SerializerMethodField queries per object class UserSerializer(serializers.ModelSerializer): order_count = serializers.SerializerMethodField() def get_order_count(self, obj): return obj.orders.count() # ← query per user # SOLUTION: Annotate in viewset, access in serializer class UserViewSet(viewsets.ModelViewSet): def get_queryset(self): return User.objects.annotate(order_count=Count('orders')) class UserSerializer(serializers.ModelSerializer): order_count = serializers.IntegerField(read_only=True) ``` ### Rule: Model properties that query are dangerous in loops ```python # PROBLEM: Property triggers query when accessed class User(models.Model): @property def recent_orders(self): return self.orders.filter(created__gte=last_week)[:5] # Used in template loop = N+1 # SOLUTION: Use Prefetch with custom queryset, or annotate ``` ### Validation Checklist for N+1 - [ ] Traced data flow from view to template/serializer - [ ] Confirmed related field is accessed inside a loop - [ ] Searched codebase for existing select_related/prefetch_related - [ ] Verified table has significant row count (1000+) - [ ] Confirmed this is a hot path (not admin, not rare action) --- ## Priority 2: Unbounded Querysets (CRITICAL) **Impact:** Loading entire tables exhausts memory. La