Query Ignore¶
The .queryignore file lets you suppress specific django-query-doctor findings. This is useful for known issues that you plan to fix later, third-party code you cannot modify, or intentional patterns that are not actually problems.
Setup¶
Create a .queryignore file in your project root (the directory containing manage.py; if none is found, the current working directory is used):
django-query-doctor checks for this file automatically. No settings changes are needed.
Where It Applies¶
.queryignore rules are applied on every surface that turns captured queries into findings:
- the middleware (per-request reports),
- the
check_queries,fix_queries, anddiagnose_projectmanagement commands, - the
diagnose_queries()context manager, - the pytest plugin fixture, and
- the Celery integration.
Every surface routes through the same analysis pipeline, so a rule behaves identically wherever findings are produced. Suppression happens at the prescription level, after analysis: the captured-query counts and timings a report shows are never altered by .queryignore -- only which findings are reported.
Changed in 2.2.0: before this release only the middleware and
fix_querieshonoured the file. Findings that pass in CI today (viacheck_queriesordiagnose_project) will start being suppressed if a matching rule exists.
Syntax¶
Each rule is one line in the form type: pattern. Four rule types exist: file, callsite, ignore, and sql. Lines starting with # and blank lines are skipped. Any line that does not contain a : is silently ignored -- there is no error for a malformed rule, so double-check your spelling.
# Suppress every finding whose callsite is in this file (glob, full path)
file: *myapp/views.py
# Suppress findings at one exact file:line
callsite: /app/myapp/views.py:42
# Suppress one issue type in files whose path contains a substring
ignore: n_plus_one:legacy_app
# Suppress findings whose description or raw SQL contains a SQL fragment
sql: %myapp_author%
file: rules¶
The pattern is matched against the prescription's callsite file path with fnmatch glob matching. The path recorded at capture time is the full path from the Python stack frame, so patterns usually need a leading *:
# All findings from views.py in myapp
file: *myapp/views.py
# All findings anywhere under legacy_app/
file: *legacy_app/*
callsite: rules¶
The pattern must equal the prescription's filepath:line_number exactly -- no globbing. Use the file path exactly as django-query-doctor prints it in its reports:
ignore: rules¶
The pattern has the form issue_type:path_substring (an optional third :-separated part is accepted and ignored). It suppresses prescriptions whose issue type matches and whose callsite path contains the given substring (plain substring, not a glob):
# Known N+1 in the legacy dashboard - tracked in JIRA-1234
ignore: n_plus_one:dashboard
# Duplicate queries in test fixtures are intentional
ignore: duplicate_query:tests/
The issue type must be one of the values below (these are the IssueType enum values, not the analyzer names):
| Issue type value | Produced by |
|---|---|
n_plus_one |
nplusone analyzer |
duplicate_query |
duplicate analyzer |
missing_index |
missing_index analyzer |
fat_select |
fat_select analyzer |
queryset_eval |
queryset_eval analyzer |
complexity |
complexity analyzer |
serializer_method_field |
serializer_method analyzer (check_serializers) |
write_n_plus_one |
write_nplusone analyzer |
Note: It is
n_plus_one, notnplusone, andduplicate_query, notduplicate. A rule written with the analyzer name instead of the issue type value never matches -- and no warning is printed.
sql: rules¶
A sql: rule matches a prescription when the pattern matches the prescription's description text or the raw SQL of any captured query behind that prescription (resolved through the prescription's fingerprint). SQL % wildcards are translated to *, and the pattern is then matched as a substring -- wrapped in *…* -- against each target. The %→* translation and this substring (non-anchored) matching apply identically to the description and the raw SQL, so one rule cannot behave differently against the two:
# Suppress findings that touch the author table (matches the table name in the SQL)
sql: %myapp_author%
# Suppress a finding whose query selects a sensitive column, even though that
# column never appears in the human-readable description
sql: %ssn_hash%
Matching against raw SQL was added in 2.2.0 and is a strict superset of the earlier description-only behaviour: every rule that matched a description before still matches. A prescription with no fingerprint -- so the queries behind it cannot be resolved -- is matched on its description alone.
Examples¶
Suppress a Known N+1 in a Legacy View¶
Suppress All Issues in Generated Code¶
Suppress Missing Index Warnings for a Small Table¶
# settings table has <100 rows, index would add overhead
ignore: missing_index:config/models.py
Use Sparingly¶
Warning: The
.queryignorefile should be a temporary measure, not a permanent fix. Every entry represents a known performance issue in your codebase. Review it regularly and remove entries as issues are resolved.
Best practices:
- Add a comment explaining why each entry exists and link to a tracking issue.
- Review periodically -- include
.queryignorereview in your sprint/quarterly planning. - Prefer fixing over ignoring. Use the Auto-Fix system to resolve common issues quickly.
- Do not ignore entire directories unless they truly contain only code you cannot modify (third-party, generated).
Further Reading¶
- Auto-Fix -- Fix issues instead of ignoring them.
- Middleware -- Where per-request suppression happens.