Skip to main content

A development tool to detect N+1 queries in Django.

Project description

Django N+1 Hunter

A powerful, zero-configuration middleware for detecting N+1 queries in your Django applications during development.

N+1 queries are the silent performance killers of Django apps. This tool automatically monitors your SQL executions and points you exactly to the line of code in your view or model that caused the problem.

Supports: Django 5.2+ and Python 3.10+ Databases: Works with PostgreSQL, MySQL, SQLite, and any other Django-supported backend.


Features

  • Automatic Interception: Hooks into Django's query execution to monitor SQL statements seamlessly across all configured databases.
  • Traceback Filtering: Analyzes the call stack to point you exactly to the line of user code that triggered the loop.
  • Async & ASGI Ready: Built using Python's contextvars, guaranteeing absolute thread safety and async isolation without data bleeding between concurrent requests.
  • Test-Suite Integration: Capable of raising exceptions rather than just logging warnings, enabling you to actively fail CI/CD pipelines when N+1 queries are introduced.
  • Production Safe: Automatically disables itself if DEBUG = False. It also raises a startup warning if accidentally deployed to production.
  • Zero Dependencies: Relies entirely on built-in Python and Django features.

Supported Database Structures

Because the hunter hooks into the database driver executions at the connection layer using Django's connection.execute_wrapper(), it is highly robust and database-structure-agnostic:

  • Standard Relations: ForeignKey, OneToOneField, and ManyToManyField (including reverse relationships).
  • Generic Relations: GenericForeignKey and reverse GenericRelations.
  • Model Inheritance: Queries generated by accessing fields on a parent table in multi-table inheritance.
  • Deferred Fields: When using .defer() or .only(), accessing a deferred field later in a loop triggers a query per object, which is successfully intercepted and flagged as an N+1.
  • Multi-Database Setups: Wraps connections.all(), so queries to replica databases or secondary write-routers are fully captured.

Installation & Setup

  1. Install the package via pip:

    pip install django-nplus1-hunter
    
  2. Add it to your INSTALLED_APPS. For safety, it is highly recommended to only add it in your local/development settings:

    # settings.py
    if DEBUG:
        INSTALLED_APPS += [
            'django_nplus1_hunter',
        ]
    
  3. Add the middleware to MIDDLEWARE. Place it near the top of the list so it can track queries generated by other middlewares (like SessionMiddleware or AuthenticationMiddleware):

    # settings.py
    if DEBUG:
        MIDDLEWARE.insert(0, 'django_nplus1_hunter.middleware.NPlus1HunterMiddleware')
    

That's it! Just browse your app locally. If a view triggers an N+1 query, your runserver console will light up with a warning showing the exact file and line number.


Configuration Options

You can customize the detection sensitivity and behavior by adding these variables to your settings.py:

# Number of queries generated from the exact same line of code before it is flagged as an N+1.
# Default: 3
NPLUS1_HUNTER_THRESHOLD = 3

# The maximum number of total queries allowed in a single request before a warning is thrown.
# Default: 50
NPLUS1_HUNTER_TOTAL_THRESHOLD = 50

# A list of URL path prefixes to completely ignore. Useful for admin panels or debug toolbars.
# Default: []
NPLUS1_HUNTER_IGNORE_PATHS = [
    '/admin/',
    '/__debug__/',
]

# If True, the middleware will raise an exception (NPlus1QueryDetectedError or HighQueryCountDetectedError)
# instead of just logging a warning. Highly recommended for running during automated testing (pytest/CI) 
# to fail the build if an N+1 is introduced.
# Default: False
NPLUS1_HUNTER_RAISE_EXCEPTION = False

Integrity & Security

This package was designed with strict security and memory integrity constraints:

  • No Data Leakage: While it intercepts both the raw SQL and its parameters, it deliberately only logs the SQL string and ignores the parameter arguments. This guarantees that sensitive user inputs (like passwords or PII) are never printed to your local terminal.
  • Thread & Async Safety: It utilizes Python's modern contextvars.ContextVar instead of threading.local(). This guarantees absolute thread-safety and prevents query data from bleeding between concurrent requests, which is crucial for ASGI and async Django deployments.
  • Memory Safe: The query tracking context is wrapped in a strict try...finally block that reliably purges memory after every request, preventing memory leaks even if views raise unexpected exceptions.
  • Precise Traceback Filtering: The package intelligently filters out Django core internals without accidentally filtering out important 3rd-party packages (like django-rest-framework), ensuring the stack trace correctly points to your view or serializer.

How it works under the hood

The middleware utilizes contextlib.ExitStack and Django's built-in connections.all()[...].execute_wrapper to wrap every database connection during the request/response lifecycle. When a query executes, the wrapper captures the raw SQL, execution time, and a full Python stack trace.

It filters out internal Django frames (django/db/models/* and django/template/*) to find the first frame belonging to your codebase. If that specific line of user code executes more times than NPLUS1_HUNTER_THRESHOLD, a warning is logged or an exception is raised.

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

django_nplus1_hunter-1.2.0.tar.gz (11.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_nplus1_hunter-1.2.0-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

File details

Details for the file django_nplus1_hunter-1.2.0.tar.gz.

File metadata

  • Download URL: django_nplus1_hunter-1.2.0.tar.gz
  • Upload date:
  • Size: 11.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_nplus1_hunter-1.2.0.tar.gz
Algorithm Hash digest
SHA256 cc72368b0008acd8bb64c8c4a636c370217da1b06462af30911505c2f9325ed1
MD5 e278ede658d75a5a2a31d1658147c15a
BLAKE2b-256 5d54683302b8dce0048664ced1075742c8aa93d0efa7be74e706dada05a2e21e

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_nplus1_hunter-1.2.0.tar.gz:

Publisher: publish.yml on iamjalipo/django-nplus1-hunter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_nplus1_hunter-1.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_nplus1_hunter-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90b7b609181670aef459c2e5956d8340c0fd0243fe82caad99056ac4211e8726
MD5 1ed4bb1b55deb24fe1e21dc8da5e7e3f
BLAKE2b-256 a37e76d4a043eeee96f3d45cd9437a2d95b8c7aaa4d776658f59d82a74b2efc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_nplus1_hunter-1.2.0-py3-none-any.whl:

Publisher: publish.yml on iamjalipo/django-nplus1-hunter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page