SJ Sarah J.
• October 14, 2025 I've been struggling with optimizing Django ORM queries for a high-traffic dashboard. We use select_related everywhere, but we are still seeing N+1 issues in some background tasks.
Hi Sarah!
While select_related handles foreign keys traversal, it doesn’t handle Many-to-Many relationships (M2M) or reverse lookups, which require prefetch_related.
If you’re debugging background tasks (like Celery workers for example), then you don’t have things like Django Debug Toolbar handy. I recommend logging query counts explicitly in your development environment or writing a test case that fails if the query count exceeds a threshold.
from django.test.utils import CaptureQueriesContext
from django.db import connection
def test_dashboard_query_count(self):
# Set up complex data...
with CaptureQueriesContext(connection) as ctx:
response = self.client.get('/dashboard/')
# Assert we don't exceed expected query count
self.assertLess(len(ctx.captured_queries), 15) We do this so much we added a helper to django-test-plus specifically for it.
Another often overlooked tool is using the --print-sql flag if you are running scripts via management commands, or simply inspecting the connection.queries list in a shell session.
Key Takeaways
select_relatedfor ForeignKey and OneToOneField (SQL JOINs)prefetch_relatedfor ManyToManyField and reverse ForeignKey (separate queries + Python joining)- Test your query counts - don’t guess, measure
- Use
django-debug-toolbarduring development, but also have tests for production code paths
Hope this helps!
View other questions about: