Skip to main content

django-nplus1

N+1 query detection for Django. The API is in beta and can still change before 1.0.

Quick Start

pip install django-nplus1
# settings.py
INSTALLED_APPS = [..., "django_nplus1"]
# settings/testing.py
MIDDLEWARE = [..., "django_nplus1.NPlus1Middleware"]
NPLUS1_RAISE = True

Adding the middleware to your test settings means every view test that goes through the Django test client will fail on N+1 queries. This catches real problems in actual request paths without false positives from helper functions or scripts that intentionally defer prefetching.

For existing projects, introducing django-nplus1 will likely surface many N+1 queries at once. Whitelist the known issues and fix them over time:

# settings/testing.py
NPLUS1_WHITELIST = [
    {"model": "myapp.Author", "field": "books"},
    {"model": "myapp.Book", "field": "publisher"},
]

The middleware can also run in development or production settings to log warnings instead of raising. See the docs for all options, including the pytest plugin and the Profiler context manager.

See examples/ for a working project.

Corpus Mode

Per-request unused_eager_load detection can produce false positives on shared prefetch patterns. Corpus mode collects eager loads across the full pytest session and only reports the ones no test read:

uv run pytest --nplus1-eager-corpus

It also reports concrete fields that were loaded but never read across the suite, as unused_field_load. Suppress noisy models with NPLUS1_FIELD_EXCLUDE = ["auth.User", "contenttypes.*"].

See docs for suppression markers and pytest-xdist support.

Celery Integration

The equivalent of the middleware for Celery tasks. Each task execution gets its own detection scope.

pip install django-nplus1[celery]
# settings.py (or settings/testing.py)
NPLUS1_CELERY = True

Lazy loads, .get()-in-a-loop, unused eager loads, and duplicate queries are all detected per-task, just as they are per-request. nplus1_allow() works inside tasks the same way it does in views.

Limitations:

  • nplus1_allow() context does not propagate across task boundaries. If a view calls task.delay() inside an nplus1_allow() block, the allow rules do not carry into the worker (ContextVars don't survive serialization).
  • A detection made when a task ends, such as an unused eager load, or one that the task catches, can't fail the task, because Celery has already recorded its result. It is logged at ERROR level on the django_nplus1 logger instead. For a task run with .apply() inside another scope, such as a request or the nplus1 test fixture, that scope reports the detection when it ends.

Credits

This project builds on the work of:

  • nplusone by Joshua Carp, the original automatic N+1 detection library for Python ORMs. django-nplus1 started as a Django-specific fork of nplusone's architecture.
  • django-zeal by Tao Bojlen, which inspired several features: deferred field detection, .get()-in-a-loop detection, ContextVar-based async safety, call-site tracking, and configurable thresholds.

License

MIT

Metadata

Release files for django-nplus1 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-nplus1 0.5.0
File Size Uploaded
django_nplus1-0.5.0.tar.gz 25.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-nplus1 0.5.0
File Interpreter ABI Platform
django_nplus1-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 57.5 kB

Release files / django_nplus1-0.5.0.tar.gz

Download URL django_nplus1-0.5.0.tar.gz
Size 25.7 kB
Tags Source
SHA-256 checksum
How to use checksums
d3bdfe3e234371058f6d8e972a240cb3233a838b0a22510a6a7c47d94bda70a6
BLAKE2b-256 checksum
How to use checksums
777d22398de50c52f2e44a62bed4f5216b3ee22c743ee699a00ee8b00976e1cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / django_nplus1-0.5.0-py3-none-any.whl

Download URL django_nplus1-0.5.0-py3-none-any.whl
Size 31.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
879ff5e83387e5bd57d0ccd6f398875583c465fe946938517a7d20adddd08959
BLAKE2b-256 checksum
How to use checksums
55dca5b8e20560be0f5f6e1ab6957daf18773f0be7d7c7b6f3de8e69cc8e1427
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page