Metadata-Version: 2.4
Name: django-query-doctor
Version: 2.3.0
Summary: Automated Django ORM query diagnosis and optimization. SQL compilation caching, prepared statements, N+1 detection, and DRF analysis.
Project-URL: Homepage, https://github.com/hassanzaibhay/django-query-doctor
Project-URL: Documentation, https://hassanzaibhay.github.io/django-query-doctor
Project-URL: Repository, https://github.com/hassanzaibhay/django-query-doctor
Project-URL: Changelog, https://github.com/hassanzaibhay/django-query-doctor/blob/main/CHANGELOG.md
Project-URL: Bug Tracker, https://github.com/hassanzaibhay/django-query-doctor/issues
Author-email: Hassan Zaib Hayat <hassanzaibhayatske@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: cache,diagnosis,django,drf,n+1,optimization,orm,performance,prepared-statements,profiling,query,rest-framework,sql
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Requires-Dist: django>=4.2
Provides-Extra: all
Requires-Dist: celery>=5.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.0; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.0; extra == 'all'
Requires-Dist: rich>=13.0; extra == 'all'
Provides-Extra: celery
Requires-Dist: celery>=5.0; extra == 'celery'
Provides-Extra: dev
Requires-Dist: asgiref>=3.7; extra == 'dev'
Requires-Dist: django-stubs>=4.2; extra == 'dev'
Requires-Dist: djangorestframework>=3.14; extra == 'dev'
Requires-Dist: factory-boy>=3.3; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pre-commit>=3.5; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest-django>=4.7; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: rich>=13.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-git-revision-date-localized-plugin>=1.2; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.0; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.0; extra == 'otel'
Provides-Extra: rich
Requires-Dist: rich>=13.0; extra == 'rich'
Description-Content-Type: text/markdown

# 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.

[![PyPI](https://img.shields.io/pypi/v/django-query-doctor.svg)](https://pypi.org/project/django-query-doctor/)
[![Tests](https://img.shields.io/github/actions/workflow/status/hassanzaibhay/django-query-doctor/ci.yml)](https://github.com/hassanzaibhay/django-query-doctor/actions)
[![Python](https://img.shields.io/pypi/pyversions/django-query-doctor.svg)](https://pypi.org/project/django-query-doctor/)
[![Django](https://img.shields.io/badge/django-4.2%20|%205.0%20|%205.1%20|%205.2%20|%206.0-blue)](https://pypi.org/project/django-query-doctor/)
[![License](https://img.shields.io/pypi/l/django-query-doctor.svg)](https://opensource.org/licenses/MIT)

## 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

```bash
pip install django-query-doctor
```

```python
# settings.py
INSTALLED_APPS = [..., "query_doctor"]
MIDDLEWARE = [..., "query_doctor.middleware.QueryDoctorMiddleware"]
```

## See It in Action

```python
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")
```

Console output (plain-text renderer; Rich adds colors and panels):

```
============================================================
Query Doctor Report
Total queries: 13 | Time: 0.2ms | Issues: 2
============================================================

CRITICAL: N+1 detected: 12 queries for table "myapp_author" (field: author)
   Location: myapp/views.py:12 in list_books
   Code: _ = book.author.name  # triggers N+1
   Fix: Add .select_related('author') to your queryset
   Queries: 12 | Est. savings: ~0.2ms

INFO: Fat SELECT: 8 columns from "myapp_book" including large fields: description
   Location: myapp/views.py:10 in list_books
   Code: books = list(Book.objects.all())
   Fix: Use .defer('description') to skip loading large fields, or .values()/.values_list() if you don't need model instances
   Queries: 1 | Est. savings: ~0.0ms
```

## 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()` |
| 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:

```python
QUERY_DOCTOR = {
    "TURBO": {"ENABLED": True}
}
```

[Full QueryTurbo guide →](https://hassanzaibhay.github.io/django-query-doctor/guides/queryturbo/)

## Requirements

- Python 3.10+
- Django 4.2, 5.0, 5.1, 5.2, or 6.0
- Optional extras: `pip install django-query-doctor[rich]` for styled console output, `[celery]` for Celery task support, `[otel]` for OpenTelemetry export
- Optional third-party packages (install separately, not query-doctor extras): DRF for serializer analysis, psycopg3 for prepared-statement support

## Links

[📖 Documentation](https://hassanzaibhay.github.io/django-query-doctor/)  |  [📦 PyPI](https://pypi.org/project/django-query-doctor/)  |  [📝 Changelog](https://hassanzaibhay.github.io/django-query-doctor/changelog/)  |  [🐛 Issues](https://github.com/hassanzaibhay/django-query-doctor/issues)

## License

MIT
