Comparison with Other Tools¶
This document provides a detailed feature comparison between django-query-doctor and other Django query analysis tools, along with guidance on when to use each tool and how they can work together.
Comparisons current as of 2026-07-14, against these releases (per each project's PyPI page):
Package Version Released django-debug-toolbar 7.0.0 2026-06-19 django-silk 5.5.0 2026-03-08 nplusone 1.0.0 2018-05-21 django-auto-prefetch 1.14.0 2025-09-18 Cells marked " -- " are capabilities the project's own documentation does not address. If a project has since gained a feature listed as missing here, please open an issue.
Feature Matrix¶
| Feature | django-query-doctor | django-debug-toolbar | django-silk | nplusone | django-auto-prefetch |
|---|---|---|---|---|---|
| Detection | |||||
| N+1 query detection | Yes | No (manual inspection) | No (manual inspection) | Yes | N/A (prevents, not detects) |
| Duplicate query detection | Yes | Partial (shows count) | No | No | No |
| Missing index detection | Yes | No | No | No | No |
| Fat SELECT detection | Yes | No | No | No | No |
| Query complexity scoring | Yes | No | No | No | No |
| DRF serializer analysis (static AST) | Yes | No | No | No | No |
| QuerySet evaluation issues | Yes | No | No | No | No |
| Diagnostics | |||||
| File:line source location | Yes | No | Partial (request only) | Partial (model only) | No |
| Auto-fix suggestions | Yes | No | No | No | N/A |
| Copy-paste fix code | Yes | No | No | No | No |
| Prescription severity levels | Yes | No | No | No | No |
| SQL fingerprinting | Yes | No | No | No | No |
| Environments | |||||
Works without DEBUG=True |
Yes | No | Yes | Yes | Yes |
| Production-safe | Yes | No | Partial (overhead) | No (dev-only per its own docs) | Yes |
| Zero required dependencies | Yes | No | No | No | No |
| Integration | |||||
| Management commands | Yes | No | No | No | No |
| Pytest plugin | Fixture only² | No | No | No¹ | No |
| Celery task support | Yes | No | Partial (profiling decorators usable in tasks) | Partial (generic Profiler context manager) |
-- |
| Async Django support | Yes³ | Partial (experimental, no concurrent requests) | -- | No (predates async Django) | -- |
| CI/CD integration | Yes | No | No | No | No |
| Git diff-aware filtering | Yes | No | No | No | No |
| Query budgets (per-view) | Yes | No | No | No | No |
| Output | |||||
| Console output | Yes | No (browser only) | No (browser only) | Yes (warnings) | No |
| JSON output | Yes | No | Yes (API) | No | No |
| HTML dashboard | Yes | Yes | Yes | No | No |
| OpenTelemetry export | Yes (manual invocation) | No | No | No | No |
| Extensibility | |||||
| Custom analyzer plugins | Yes | No | No | No | No |
| Custom reporter plugins | No | Yes (panels) | No | No | No |
| Ignore rules | Yes | No | Yes | Yes | No |
| Configuration | |||||
| Zero-config setup | Yes | Partial | No | Yes | Yes |
| Per-view configuration | Partial (per-view @query_budget; analyzer config is global) |
No | No | No | No |
| Sampling support | Yes | No | Yes | No | No |
¹ nplusone ships no pytest plugin or fixtures; failing automated tests on N+1 is
supported first-party via NPLUSONE_RAISE = True (raises NPlusOneError, "to force
all automated tests involving unnecessary queries to fail") or the core Profiler
context manager (README).
² django-query-doctor registers a pytest11 entry point providing a query_doctor
fixture. The fixture's report is populated at test teardown, so in-test assertions on
it see an empty report; its findings are instead surfaced at end of session by a
pytest_terminal_summary hook that prints one line per test with findings
(src/query_doctor/pytest_plugin.py:140, emitting line at :167). For working
in-test assertions, use diagnose_queries() (see the
pytest guide).
³ From 2.1.2 onwards. Queries from both async def and sync views are captured
under a real ASGI handler; see tests/test_asgi_middleware_chain.py, which drives
django.core.handlers.asgi.ASGIHandler across Django's startproject middleware
defaults and asserts the captured query count, not just the response status.
2.1.1 and earlier either crashed or captured nothing under ASGI -- see
Async Support.
Tool-by-Tool Comparison¶
vs. django-debug-toolbar¶
django-debug-toolbar is the most widely used Django debugging tool. It provides a browser-based panel showing SQL queries, template rendering, cache hits, signals, and more.
Strengths of debug-toolbar: - Comprehensive debugging beyond just SQL (templates, cache, signals, headers) - Interactive browser UI with collapsible query details - Mature ecosystem with many third-party panels - Shows EXPLAIN output for individual queries
Where django-query-doctor adds value:
- Automatic pattern detection (N+1, duplicates) instead of manual inspection
- Works in non-browser contexts (API endpoints, management commands, Celery tasks)
- Works without DEBUG=True, enabling staging and production analysis
- Generates fix suggestions, not just query lists
- CI/CD integration for automated regression detection
Tip: These tools solve different problems. debug-toolbar is an interactive debugging tool; django-query-doctor is an automated analysis tool. They work well together. See "Using Tools Together" below.
vs. django-silk¶
django-silk is a profiling and request inspection tool that stores request/response data and SQL queries in the database for later analysis.
Strengths of django-silk: - Persistent storage of profiling data across requests - Code profiling (cProfile integration) beyond just SQL - Request/response body inspection - Historical comparison of request performance
Where django-query-doctor adds value: - No database tables or storage overhead required - Automatic issue detection instead of manual analysis - Fix suggestions with exact code changes - Lighter weight: no UI server, no database writes per request - CI/CD integration
vs. nplusone¶
nplusone is the closest existing tool in concept. It specifically detects N+1 queries by monitoring lazy-loaded relationship access.
Strengths of nplusone:
- Focused, simple implementation
- Low overhead for its specific use case
- Can fail automated tests via NPLUSONE_RAISE (raises NPlusOneError)
- Configurable whitelist (NPLUSONE_WHITELIST, exact names or fnmatch patterns) and a generic Profiler context manager for use outside HTTP requests
- Also detects unused eager loading (related data loaded but never accessed)
Where django-query-doctor adds value: - Six additional detection categories beyond N+1 - Exact file:line references (nplusone reports the model/relationship only) - Copy-paste fix code generation - Management commands for full-project scanning - Query budgets and CI/CD enforcement - Git diff-aware filtering for incremental adoption - Designed to run in production; nplusone's own documentation says it should only be used in development
Note: nplusone's most recent release is 1.0.0, published in May 2018 (per its PyPI page) -- the project has been unmaintained for years, though it remains installable and functional on the ORMs of its era.
vs. django-auto-prefetch¶
django-auto-prefetch takes a fundamentally different approach: instead of detecting and reporting issues, it automatically prefetches to-one relations (ForeignKey and OneToOneField, including reverse OneToOne) at the model level, so accessing an unfetched relation triggers a single follow-up query for the whole batch instead of one query per instance.
Strengths of django-auto-prefetch:
- Zero developer effort after initial setup
- Immediate performance improvement for ForeignKey/OneToOne N+1
- No reports to read or fixes to apply
- Written by Gordon Wrigley (GitHub: tolomea) and maintained by Adam Johnson (adamchainz), per its PyPI page -- as of 1.14.0 it supports Django 4.2-6.0 and Python 3.9-3.14
Where django-query-doctor adds value: - Visibility into what queries ran and why (auto-prefetch's mitigation happens silently) - Handles ManyToMany and reverse-FK relationships (auto-prefetch handles ForeignKey/OneToOne only) - Detects issues beyond N+1 (duplicates, missing indexes, fat SELECTs, etc.) - Per-view control instead of a global model-level behavior change - Helps developers learn to write better querysets, since it reports rather than silently mitigates
Note: django-auto-prefetch changes query behavior at the model level for every access to an unfetched to-one field, application-wide. That is the intended trade-off -- automatic mitigation instead of manual
select_related()calls -- and the same on-demand-prefetch idea is coming to Django core as fetch modes (FETCH_PEERS), new in Django 6.1 -- see the linked release notes for its current status. django-query-doctor takes the opposite approach: report and suggest a fix rather than change behavior automatically. Both are valid; pick based on whether you want automatic mitigation or explicit, reviewable fixes.
When to Use What¶
Use django-query-doctor when:¶
- You want to systematically find and fix query issues across your project
- You need CI/CD enforcement to prevent query regressions
- You are working on API endpoints (not just browser-rendered pages)
- You need to analyze queries in staging or production (without
DEBUG=True) - You want prescriptive fixes, not just detection
- You are onboarding a team to better ORM practices
Use django-debug-toolbar when:¶
- You are actively debugging a specific page in the browser
- You need to inspect template rendering, cache behavior, or signals
- You want to run EXPLAIN on a specific query interactively
- You are in local development with
DEBUG=True
Use django-silk when:¶
- You need to profile Python code execution (not just SQL)
- You want to store and compare historical request performance
- You need to inspect request/response bodies
- You have a dedicated profiling environment
Use nplusone when:¶
- You only care about N+1 detection and want a minimal tool
- You want N+1 detection as pytest warnings without any setup
Use django-auto-prefetch when:¶
- You want an immediate fix for ForeignKey N+1 without changing any view code
- You understand and accept the trade-off of invisible query modification
- Your N+1 issues are predominantly on ForeignKey relationships (not M2M)
Using Tools Together¶
These tools are not mutually exclusive. Here are recommended combinations:
Development Stack¶
# settings/local.py
INSTALLED_APPS = [
# ...
"debug_toolbar", # Interactive browser debugging
]
MIDDLEWARE = [
"debug_toolbar.middleware.DebugToolbarMiddleware", # Browser UI
"query_doctor.middleware.QueryDoctorMiddleware", # Automated analysis
# ...
]
Use debug-toolbar for interactive exploration and django-query-doctor for automated detection. When debug-toolbar shows you a page with many queries, django-query-doctor tells you exactly which ones are problems and how to fix them.
CI Stack¶
# .github/workflows/ci.yml
steps:
- name: Run tests with query assertions
run: pytest -v
- name: Endpoint scan
run: python manage.py check_queries --url /api/books/ --format json --fail-on warning
In CI, only django-query-doctor and nplusone can run (debug-toolbar and silk require a browser/server). Between the two, django-query-doctor detects more issue categories and can fail a build on a severity threshold or on a regression against a recorded baseline; nplusone raises or logs on the N+1 patterns it detects. Which of those you want depends on whether you are gating a build or annotating a test run.
Production Stack¶
# settings/production.py
QUERY_DOCTOR = {
"ENABLED": True,
"SAMPLE_RATE": 0.01, # 1% of requests
"REPORTERS": ["log"], # Route findings into your log pipeline
"CAPTURE_STACK_TRACES": False, # Minimize overhead
}
In production, only django-query-doctor and django-auto-prefetch are suitable (nplusone's own docs say it should not be deployed to production). django-auto-prefetch provides automatic mitigation; django-query-doctor provides visibility and detection. They can coexist: auto-prefetch handles ForeignKey/OneToOne N+1 automatically while django-query-doctor catches everything else (M2M, duplicates, missing indexes, etc.).
Migration Guide¶
From django-debug-toolbar (adding django-query-doctor)¶
No migration needed. Keep debug-toolbar for interactive debugging and add django-query-doctor alongside it:
MIDDLEWARE = [
"debug_toolbar.middleware.DebugToolbarMiddleware",
"query_doctor.middleware.QueryDoctorMiddleware",
# ...
]
From nplusone to django-query-doctor¶
- Replace
nplusone.ext.django.NPlusOneMiddlewarewithquery_doctor.middleware.QueryDoctorMiddleware. - Remove
nplusonefromINSTALLED_APPSandMIDDLEWARE. - django-query-doctor covers all of nplusone's detection and more.
# Before
INSTALLED_APPS = ["nplusone.ext.django", ...]
MIDDLEWARE = ["nplusone.ext.django.NPlusOneMiddleware", ...]
# After
MIDDLEWARE = ["query_doctor.middleware.QueryDoctorMiddleware", ...]
From django-auto-prefetch (adding django-query-doctor)¶
Keep django-auto-prefetch if it is working well for you. Add django-query-doctor to catch the issues auto-prefetch does not handle (M2M, reverse-FK, duplicates, missing indexes, etc.):
MIDDLEWARE = [
"query_doctor.middleware.QueryDoctorMiddleware",
# ...
]
# django-auto-prefetch is configured at the model level, not in middleware
Note: With both tools active, django-query-doctor will not report N+1 issues on ForeignKey/OneToOne relationships that django-auto-prefetch has already resolved. You will still see reports for M2M relationships and other issue categories.
Summary Table¶
| Scenario | Recommended Tool(s) |
|---|---|
| Find all query issues in a project | django-query-doctor |
| Interactive page debugging | django-debug-toolbar |
| CI/CD query regression prevention | django-query-doctor |
| Production query monitoring | django-query-doctor (sampled) |
| Quick ForeignKey N+1 mitigation | django-auto-prefetch |
| Python code profiling | django-silk |
| Minimal N+1 detection only | nplusone |
| Comprehensive development setup | django-debug-toolbar + django-query-doctor |
| Comprehensive production setup | django-auto-prefetch + django-query-doctor (sampled) |
For details on django-query-doctor's architecture, see Architecture. For performance characteristics, see Performance. For the reasoning behind these design choices, see Background & Design.