Metadata-Version: 2.5
Name: django-nplus1
Version: 0.4.0
Summary: N+1 query detection for Django
Project-URL: Homepage, https://github.com/oliverhaas/django-nplus1
Project-URL: Documentation, https://oliverhaas.github.io/django-nplus1/
Project-URL: Repository, https://github.com/oliverhaas/django-nplus1.git
Project-URL: Changelog, https://oliverhaas.github.io/django-nplus1/reference/changelog/
Author-email: Oliver Haas <ohaas@e1plus.de>
License-Expression: MIT
License-File: LICENSE
Keywords: detection,django,n+1,orm,performance,query
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 6.0
Classifier: Framework :: Django :: 6.1
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: django<7,>=6
Provides-Extra: celery
Requires-Dist: celery>=5.3; extra == 'celery'
Description-Content-Type: text/markdown

# django-nplus1

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

## Quick Start

```bash
pip install django-nplus1
```

```python
# settings.py
INSTALLED_APPS = [..., "django_nplus1"]
```

```python
# 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:

```python
# 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](https://oliverhaas.github.io/django-nplus1/) for all options, including the pytest plugin and the `Profiler` context manager.

See [examples/](https://github.com/oliverhaas/django-nplus1/tree/main/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:

```bash
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](https://oliverhaas.github.io/django-nplus1/user-guide/corpus-mode/) 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.

```bash
pip install django-nplus1[celery]
```

```python
# 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, can't fail the task, because Celery has already recorded its result. It is logged at ERROR level on the `django_nplus1` logger instead.

## Credits

This project builds on the work of:

- [nplusone](https://github.com/jmcarp/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](https://github.com/taobojlen/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
