API Reference¶
Auto-generated documentation
This page uses mkdocstrings to render API documentation directly from source code docstrings. Some references may not render if module paths differ from the documented structure.
Core¶
QueryDoctorMiddleware¶
The Django middleware that intercepts queries for each request and runs them through the analysis pipeline.
QueryDoctorMiddleware(get_response)
¶
Django middleware that activates query diagnosis per request.
Installs an execute_wrapper on the database connection to capture all SQL queries. After the response is generated, runs all enabled analyzers and sends reports to configured reporters.
Supports both sync and async views via sync_capable and async_capable.
Initialize the middleware.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
get_response
|
Callable[..., Any]
|
The next middleware or view in the chain. |
required |
Source code in src/query_doctor/middleware.py
__acall__(request)
async
¶
Process an async request through the query doctor pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
HttpRequest
|
The incoming HTTP request. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The HTTP response from the async view. |
Source code in src/query_doctor/middleware.py
__call__(request)
¶
Process a request through the query doctor pipeline.
Routes to sync or async path based on the get_response type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
HttpRequest
|
The incoming HTTP request. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The HTTP response from the view. |
Source code in src/query_doctor/middleware.py
Context Managers¶
Context managers for targeted query analysis outside of the middleware.
context_managers
¶
Context managers for targeted query diagnosis.
Provides diagnose_queries() for diagnosing queries within a specific code block rather than an entire request.
diagnose_queries()
¶
Context manager for targeted query diagnosis.
Captures and analyzes all SQL queries executed within the context. The DiagnosisReport is yielded and populated after the context exits.
Usage
with diagnose_queries() as report: # ... your ORM code here ... print(report.issues)
Source code in src/query_doctor/context_managers.py
Decorators¶
Function and method decorators for query diagnosis and budgets.
decorators
¶
Decorators for query diagnosis and budget enforcement.
Provides @diagnose for wrapping functions with automatic query analysis, and @query_budget for enforcing query count and time limits.
diagnose(func)
¶
Decorator that diagnoses queries executed within a function.
Wraps the function with diagnose_queries() context manager. After execution, the DiagnosisReport is attached as func._query_doctor_report.
Usage
@diagnose def my_view(request): return Book.objects.all()
Source code in src/query_doctor/decorators.py
query_budget(max_queries=None, max_time_ms=None)
¶
Decorator that enforces query budget limits on a function.
Raises QueryBudgetError if the function exceeds the specified query count or time limits. Falls back to config defaults if no explicit limits are provided.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_queries
|
int | None
|
Maximum number of queries allowed. None means no limit. |
None
|
max_time_ms
|
float | None
|
Maximum total query time in milliseconds. None means no limit. |
None
|
Usage
@query_budget(max_queries=10, max_time_ms=100) def my_view(request): return Book.objects.all()
Source code in src/query_doctor/decorators.py
Data Types¶
Severity¶
Severity levels for prescriptions.
Severity
¶
Bases: Enum
Severity level for a diagnosed issue.
Prescription¶
The data class returned by analyzers describing a detected issue and its fix.
Prescription(issue_type, severity, description, fix_suggestion, callsite, query_count=0, time_saved_ms=0, fingerprint='', extra=dict())
dataclass
¶
A diagnosed issue with an actionable fix.
CapturedQuery¶
Information captured for each SQL query during interception.
CapturedQuery(sql, params, duration_ms, fingerprint, normalized_sql, callsite, is_select, tables)
dataclass
¶
A single SQL query captured during a request.
DiagnosisReport¶
Aggregated report of all prescriptions for a request or command.
DiagnosisReport(prescriptions=list(), total_queries=0, total_time_ms=0, captured_queries=list())
dataclass
¶
Analyzers¶
BaseAnalyzer¶
The abstract base class that all analyzers implement. Subclass this to create custom analyzers.
BaseAnalyzer
¶
Bases: ABC
Base class for all query analyzers.
analyze(queries, models_meta=None)
abstractmethod
¶
Analyze captured queries and return prescriptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured SQL queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional Django model metadata for enhanced analysis. |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of Prescription objects describing detected issues and fixes. |
Source code in src/query_doctor/analyzers/base.py
is_enabled()
¶
Check if this analyzer is enabled in config.
Source code in src/query_doctor/analyzers/base.py
NPlusOneAnalyzer¶
Detects N+1 query patterns using fingerprint-based grouping.
NPlusOneAnalyzer
¶
Bases: BaseAnalyzer
Analyzer that detects N+1 query patterns.
Groups queries by fingerprint and identifies repeated SELECT queries that indicate missing select_related() or prefetch_related() calls.
analyze(queries, models_meta=None)
¶
Analyze queries for N+1 patterns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata (not used currently). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected N+1 issues. |
Source code in src/query_doctor/analyzers/nplusone.py
DuplicateAnalyzer¶
Detects exact and near-duplicate queries within a single request.
DuplicateAnalyzer
¶
Bases: BaseAnalyzer
Analyzer that detects exact and near-duplicate queries.
Exact duplicates: same SQL + same params executed multiple times. Near-duplicates: same SQL structure (fingerprint) with different params.
analyze(queries, models_meta=None)
¶
Analyze queries for duplicate patterns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata (not used). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected duplicate issues. |
Source code in src/query_doctor/analyzers/duplicate.py
MissingIndexAnalyzer¶
Detects queries filtering or ordering on non-indexed columns.
MissingIndexAnalyzer
¶
Bases: BaseAnalyzer
Analyzer that detects queries on non-indexed columns.
Examines WHERE and ORDER BY clauses to find columns that lack database indexes, and suggests adding Meta.indexes with models.Index().
analyze(queries, models_meta=None)
¶
Analyze queries for missing index issues.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata (not used). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected missing index issues. |
Source code in src/query_doctor/analyzers/missing_index.py
FatSelectAnalyzer¶
Detects queries selecting more columns than necessary.
FatSelectAnalyzer(field_count_threshold=None)
¶
Bases: BaseAnalyzer
Analyzer that detects overly broad SELECT queries.
Flags queries that select many columns and suggests using .only() or .defer() to reduce data transfer.
Initialize the analyzer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field_count_threshold
|
int | None
|
Minimum number of columns to flag. Defaults to config or 8. |
None
|
Source code in src/query_doctor/analyzers/fat_select.py
analyze(queries, models_meta=None)
¶
Analyze queries for fat SELECT patterns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata (not used currently). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected fat SELECT issues. |
Source code in src/query_doctor/analyzers/fat_select.py
QuerySetEvalAnalyzer¶
Detects unintended queryset evaluation patterns.
QuerySetEvalAnalyzer
¶
Bases: BaseAnalyzer
Analyzer that detects inefficient queryset evaluation patterns.
Inspects call site code context to find patterns where Django provides more efficient alternatives (count, exists, first).
analyze(queries, models_meta=None)
¶
Analyze queries for inefficient evaluation patterns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata (not used). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected evaluation issues. |
Source code in src/query_doctor/analyzers/queryset_eval.py
DRFSerializerAnalyzer¶
Detects N+1 patterns caused by DRF serializer nesting.
DRFSerializerAnalyzer
¶
Bases: BaseAnalyzer
Analyzer that detects DRF views missing queryset optimizations.
Inspects DRF serializer classes for nested serializers and checks whether the corresponding queryset uses select_related or prefetch_related for those relations.
analyze(queries, models_meta=None)
¶
Analyze captured queries for DRF serializer issues.
Note: This analyzer primarily works through analyze_view() which is called with DRF-specific context. The query-based analyze() method serves as a fallback interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional model metadata. |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions (empty for query-only analysis). |
Source code in src/query_doctor/analyzers/drf_serializer.py
analyze_view(view_class=None, serializer_class=None, queryset=None)
¶
Analyze a DRF view for missing queryset optimizations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
view_class
|
Any
|
The DRF ViewSet or APIView class. |
None
|
serializer_class
|
Any
|
The serializer class used by the view. |
None
|
queryset
|
Any
|
The queryset used by the view. |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of prescriptions for detected issues. |
Source code in src/query_doctor/analyzers/drf_serializer.py
QueryComplexityAnalyzer¶
Scores queries by complexity and flags those above threshold.
QueryComplexityAnalyzer
¶
Bases: BaseAnalyzer
Analyzes SQL queries for excessive complexity.
Scores queries based on structural patterns (JOINs, subqueries, aggregations, etc.) and flags those exceeding a configurable threshold.
analyze(queries, models_meta=None)
¶
Analyze captured queries for excessive complexity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[CapturedQuery]
|
List of captured SQL queries to analyze. |
required |
models_meta
|
dict[str, Any] | None
|
Optional Django model metadata (unused). |
None
|
Returns:
| Type | Description |
|---|---|
list[Prescription]
|
List of Prescription objects for overly complex queries. |
Source code in src/query_doctor/analyzers/complexity.py
Reporters¶
ConsoleReporter¶
Terminal output with Rich formatting and plain-text fallback.
ConsoleReporter(stream=None, group_by=None)
¶
Formats and prints diagnosis reports to the console.
Uses Rich for styled output if available, otherwise plain text. Supports grouped output mode for related prescriptions.
Initialize the console reporter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
Any
|
Output stream (file-like object). Defaults to sys.stderr. Accepts TextIO, Django's OutputWrapper, or any writable stream. |
None
|
group_by
|
str | None
|
If set, group prescriptions by this strategy ("file_analyzer", "root_cause", "view"). |
None
|
Source code in src/query_doctor/reporters/console.py
render(report)
¶
Render a diagnosis report as a formatted string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to render. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Formatted string representation of the report. |
Source code in src/query_doctor/reporters/console.py
report(report)
¶
Print the diagnosis report to the configured stream.
If group_by was set during init, groups related prescriptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to print. |
required |
Source code in src/query_doctor/reporters/console.py
JSONReporter¶
Structured JSON output for CI/CD pipelines.
JSONReporter(output_path=None)
¶
Formats diagnosis reports as structured JSON.
Optionally writes the JSON to a file for CI/CD integration.
Initialize the JSON reporter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_path
|
str | None
|
Optional file path to write JSON output. If None, output is only available via render(). |
None
|
Source code in src/query_doctor/reporters/json_reporter.py
render(report)
¶
Render a diagnosis report as a JSON string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to render. |
required |
Returns:
| Type | Description |
|---|---|
str
|
JSON string representation of the report. |
Source code in src/query_doctor/reporters/json_reporter.py
report(report)
¶
Write the diagnosis report to the configured output path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to output. |
required |
Source code in src/query_doctor/reporters/json_reporter.py
HTMLReporter¶
Interactive HTML dashboard report.
HTMLReporter(output_path=None)
¶
Generates standalone HTML reports for query diagnosis.
Produces a single HTML file with inline CSS suitable for saving, sharing, or viewing in a browser.
Initialize the HTML reporter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_path
|
str | None
|
Optional file path to write HTML output. |
None
|
Source code in src/query_doctor/reporters/html_reporter.py
render(report)
¶
Render a diagnosis report as a standalone HTML string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to render. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Complete HTML document as a string. |
Source code in src/query_doctor/reporters/html_reporter.py
report(report)
¶
Write the diagnosis report to the configured output path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to output. |
required |
Source code in src/query_doctor/reporters/html_reporter.py
LogReporter¶
Python logging integration for production monitoring.
LogReporter
¶
Sends diagnosis reports to Python's logging system.
Each prescription is logged at the appropriate level based on severity.
report(report)
¶
Log the diagnosis report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to log. |
required |
Source code in src/query_doctor/reporters/log_reporter.py
OTelReporter¶
OpenTelemetry span export for observability platforms.
OTelReporter(tracer=None)
¶
Reports query diagnosis data via OpenTelemetry spans.
Creates a span for each diagnosis run with attributes for summary metrics and events for each prescription. Sets span status to ERROR if critical issues are found.
If OpenTelemetry is not installed, all operations are no-ops.
Initialize the OTel reporter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tracer
|
Any
|
Optional pre-configured OTel tracer. If None, one is created from the global TracerProvider. |
None
|
Source code in src/query_doctor/reporters/otel_exporter.py
has_otel
property
¶
Whether OpenTelemetry is available.
report(report)
¶
Export diagnosis report as OTel span data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
DiagnosisReport
|
The diagnosis report to export. |
required |
Source code in src/query_doctor/reporters/otel_exporter.py
Configuration¶
get_config¶
Access django-query-doctor settings with defaults.
get_config()
cached
¶
Return the merged configuration, cached after first call.
Reads the QUERY_DOCTOR setting from Django settings and deep-merges it with DEFAULT_CONFIG. The result is cached for performance.
Source code in src/query_doctor/conf.py
Fingerprinting¶
SQL Fingerprinting¶
Normalize and hash SQL statements for grouping.
fingerprint
¶
SQL normalization and fingerprinting for query pattern detection.
Provides functions to normalize SQL queries (replacing literals with placeholders), generate deterministic fingerprints for grouping similar queries, and extract table names from SQL statements.
extract_tables(sql)
¶
Extract table names from FROM and JOIN clauses in a SQL query.
Handles: FROM table, JOIN table, FROM table AS alias, FROM "quoted_table", and subqueries. Returns a deduplicated list of table names (without quotes or aliases).
Source code in src/query_doctor/fingerprint.py
fingerprint(sql)
¶
Generate a SHA-256 fingerprint (first 16 hex chars) for a SQL query.
Two queries with the same structure but different parameter values will produce the same fingerprint.
Source code in src/query_doctor/fingerprint.py
normalize_sql(sql)
¶
Normalize a SQL query by replacing literals with placeholders.
Replaces quoted strings, numbers, booleans, and IN-clause lists with '?', collapses whitespace, strips semicolons, and lowercases everything.
Source code in src/query_doctor/fingerprint.py
Stack Tracing¶
Source Code Mapping¶
Map captured queries back to user source code locations.
stack_tracer
¶
Stack trace capture for mapping SQL queries to user source code.
Walks the call stack to find the first frame in user code (filtering out Django internals, this package, and stdlib modules) so each query can be attributed to a specific file:line in the application.
capture_callsite(exclude_modules=None)
¶
Walk the stack and find the first frame in user code.
Filters out frames from query_doctor, Django internals, and stdlib. Returns the last remaining frame (closest to the query trigger), or None if no user code frame is found.
Source code in src/query_doctor/stack_tracer.py
Exceptions¶
All exceptions raised by django-query-doctor inherit from QueryDoctorError:
exceptions
¶
Exception hierarchy for django-query-doctor.
All package exceptions inherit from QueryDoctorError to allow callers to catch any query-doctor-specific error with a single except clause.
AnalyzerError
¶
Bases: QueryDoctorError
Raised when an analyzer encounters an error.
ConfigError
¶
Bases: QueryDoctorError
Raised when there is a configuration error.
InterceptorError
¶
Bases: QueryDoctorError
Raised when the query interceptor encounters an error.
QueryBudgetError(message, report=None)
¶
Bases: QueryDoctorError
Raised when a function exceeds its query budget.
Attributes:
| Name | Type | Description |
|---|---|---|
report |
The DiagnosisReport from the function execution. |
Initialize with a message and optional report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Human-readable description of the budget violation. |
required |
report
|
DiagnosisReport | None
|
The DiagnosisReport from the function execution. |
None
|
Source code in src/query_doctor/exceptions.py
QueryDoctorError
¶
Bases: Exception
Base exception for all django-query-doctor errors.