Skip to content

shipwell/nplusone-django

 
 

Repository files navigation

nplusone-django

Detect N+1 query patterns in Django ORM code via test-suite runs (primary) or per-request profiling on a local development server (secondary).

This project is a hard fork of jmcarp/nplusone, which has been unmaintained since 2018. It is not a drop-in replacement for the upstream package and does not attempt to track upstream changes or preserve the upstream API surface outside of the Django code paths.

Scope of the fork

What this fork keeps from upstream:

  • The ORM monkey-patch + blinker signal architecture that drives detection.
  • The LazyListener heuristic for spotting lazy loads on already-fetched rows.
  • The EagerListener machinery for detecting unused eager loads (kept but disabled in v1).
  • The Django middleware (NPlusOneMiddleware) for per-request detection on a local development server.

What this fork explicitly drops:

  • SQLAlchemy support (nplusone.ext.sqlalchemy).
  • Flask-SQLAlchemy support (nplusone.ext.flask_sqlalchemy).
  • Peewee support (nplusone.ext.peewee).
  • Generic WSGI middleware (nplusone.ext.wsgi).
  • The Python 2 / six compatibility layer.
  • Support for Django < 4.2.

What this fork adds (planned):

  • A custom Django TEST_RUNNER that scopes detection per test and emits a structured Finding report (JSON + Markdown) as a CI artifact.
  • A checked-in ignore list with reason-field auditing.
  • A rewritten stack-frame walker that captures the full user frame with source snippet (replacing upstream's single-frame heuristic).

No upstream compatibility intent. This project will not be contributed back to jmcarp/nplusone, will not match the upstream version-numbering scheme, and may freely break upstream-compatible patterns when a Django-only implementation is cleaner.

Status

Bootstrap complete. The fork is renamed to nplusone_django, the package builds with uv, lint and format are managed by ruff, the bundled test suite uses Django's unittest-based runner, and the non-Django code paths are removed. The structured test-suite detection (custom runner + reporter + ignore list) is planned but not yet implemented.

Installation

Once a release is published this section will list the install commands. Pre-release, install from a git pin or path dependency.

Compatibility

  • Python 3.10+
  • Django 4.2 LTS, 5.0, 5.1

Usage

Per-request detection (dev only — never enable in production). Add the middleware to your Django settings:

INSTALLED_APPS = [
    # ...
    "nplusone_django",
]
MIDDLEWARE = [
    # ...
    "nplusone_django.NPlusOneMiddleware",
]
NPLUSONE_LOGGER = logging.getLogger("nplusone")
NPLUSONE_LOG_LEVEL = logging.WARN

When a potential N+1 is detected during a request, it is logged via NPLUSONE_LOGGER. Set NPLUSONE_RAISE = True to raise NPlusOneError instead.

Test-suite detection (CI). Planned; not yet available.

Whitelisting

Suppress specific patterns via NPLUSONE_WHITELIST in settings:

NPLUSONE_WHITELIST = [
    {"model": "myapp.Carrier", "field": "user"},
    {"model": "myapp.*"},  # fnmatch on app_label.ModelName
]

License

MIT, preserved from upstream. See LICENSE. Upstream copyright is preserved in AUTHORS.rst.

Credit

The detection heuristics, ORM monkey-patches, and signal architecture in this fork are the work of Joshua Carp (@jmcarp) and the upstream contributors. See AUTHORS.rst.

About

Auto-detecting the n+1 queries problem in Django

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Python 100.0%