Automated Django ORM query diagnosis and optimization. SQL compilation caching, prepared statements, N+1 detection, and DRF analysis.
Project description
django-query-doctor
Diagnose and fix slow Django ORM queries. Detects N+1s, duplicates, missing indexes, and more — with exact file:line references and actionable fixes.
The Problem
Every Django app accumulates hidden query inefficiencies — N+1 loops behind serializers, duplicate fetches scattered across views, full table scans on unindexed columns. django-query-doctor intercepts queries at runtime using connection.execute_wrapper(), runs them through 8 analyzers, and produces prescriptions with the exact file, line, and code fix. It works in middleware, tests, CI pipelines, and management commands — no DEBUG=True required.
Install
pip install django-query-doctor
# settings.py
INSTALLED_APPS = [..., "query_doctor"]
MIDDLEWARE = [..., "query_doctor.middleware.QueryDoctorMiddleware"]
See It in Action
from query_doctor.context_managers import diagnose_queries
with diagnose_queries() as report:
books = list(Book.objects.all())
for book in books:
_ = book.author.name # triggers N+1
assert report.issues > 0
print(f"Found {report.issues} issues in {report.total_queries} queries")
Output:
[CRITICAL] N+1 Query
50 queries fetching Author for each Book.
Location: views.py:42
Fix: Add select_related('author') to queryset
What It Detects
| Issue | What It Catches |
|---|---|
| N+1 Queries | Related objects loaded one-per-row in loops |
| Duplicate Queries | Same SQL executed multiple times per request |
| Missing Indexes | Filters on columns without database indexes |
| Fat SELECT | Fetching all columns when only a few are used |
| QuerySet Evaluation | len(qs) instead of qs.count(), bool(qs) instead of qs.exists() |
| DRF Serializer | N+1 from nested serializers or missing select_related |
| Query Complexity | Excessive JOINs, subqueries, or OR chains |
| SerializerMethodField | AST analysis of get_<field> method bodies for hidden N+1s |
Every prescription includes: severity, file:line, and the exact code fix.
QueryTurbo (v2.0)
QueryTurbo reduces SQL compilation overhead by caching compiled query structures and extracting parameters directly from Django's Query tree, bypassing repeated calls to SQLCompiler.as_sql(). Queries are validated across 3 executions before the compilation step is skipped entirely. Enable it in settings:
QUERY_DOCTOR = {
"TURBO": {"ENABLED": True}
}
Requirements
- Python 3.10+
- Django 4.2, 5.0, 5.1, 5.2, or 6.0
- Optional: Rich (styled console), DRF (serializer analysis), psycopg3 (prepared statements)
Links
📖 Documentation | 📦 PyPI | 📝 Changelog | 🐛 Issues
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_query_doctor-2.0.0.tar.gz.
File metadata
- Download URL: django_query_doctor-2.0.0.tar.gz
- Upload date:
- Size: 262.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2ea8085be8f7219789f7d8c707b527107f47e72c47323a390ea5b6ea3e6cbe1
|
|
| MD5 |
2466e3dc3a54a0e27a03a455f8433c95
|
|
| BLAKE2b-256 |
56e707e54e6869ffe5b2ca5e2afb4496875fd90284eb86fd5a29524512231c4f
|
File details
Details for the file django_query_doctor-2.0.0-py3-none-any.whl.
File metadata
- Download URL: django_query_doctor-2.0.0-py3-none-any.whl
- Upload date:
- Size: 118.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee5af806e9728160ae2e9b9509d4ed62142472d647c89c46e19515aa8dacf22c
|
|
| MD5 |
ac5d4e747ba6630a6d0062781fb16d34
|
|
| BLAKE2b-256 |
b5e6e1cb11700a0721c8a1d78020b4cc4da531207e3ac147bff1516044881ec6
|