python.django.orm_select_related
Performance
Medium
Detects ORM queries followed by relationship access in loops without select_related() or prefetch_related().
Why It Matters
Section titled “Why It Matters”Django’s ORM uses lazy loading by default:
- N+1 queries — Each relationship access triggers a new query
- Database overload — 100 objects with a relationship = 101 queries
- Slow page loads — Latency compounds with each query
- Hidden until production — Small datasets hide the problem
This is one of the most common Django performance issues.
Example
Section titled “Example”# ❌ Before (N+1 queries)users = User.objects.all()for user in users: print(user.profile.avatar) # Query per user! for order in user.orders.all(): # Another query per user! print(order.total)100 users = 1 + 100 + 100 = 201 queries.
# ✅ After (2 queries)users = User.objects.select_related('profile').prefetch_related('orders')for user in users: print(user.profile.avatar) # No extra query for order in user.orders.all(): # No extra query print(order.total)What Unfault Detects
Section titled “What Unfault Detects”- QuerySet iteration followed by relationship access
- Missing
select_relatedfor ForeignKey/OneToOne - Missing
prefetch_relatedfor ManyToMany/reverse FK - Nested relationship access patterns
Auto-Fix
Section titled “Auto-Fix”Unfault adds appropriate select_related() or prefetch_related() calls.
When to Use Which
Section titled “When to Use Which”# select_related: ForeignKey and OneToOneField# Uses SQL JOIN - one query totalUser.objects.select_related('profile', 'company')
# prefetch_related: ManyToManyField and reverse ForeignKey# Uses separate query - 2 queries totalUser.objects.prefetch_related('orders', 'permissions')
# Combine themUser.objects.select_related('profile').prefetch_related('orders')
# Complex prefetch with filteringfrom django.db.models import Prefetch
User.objects.prefetch_related( Prefetch('orders', queryset=Order.objects.filter(status='pending')))