Registry / database / nplusone

nplusone

JSON →
library1.0.0pypypi✓ verified 84d ago

Nplusone is a Python library designed to detect N+1 query problems in Object-Relational Mappers (ORMs) during development. It supports popular ORMs like SQLAlchemy, Peewee, and the Django ORM. The library monitors database interactions and emits warnings or raises exceptions when potentially inefficient lazy loads or unnecessary eager loads are detected. The current version is 1.0.0, released in May 2018, and it appears to have a low release cadence, indicating a mature and stable project.

pip install nplusone
INSTALL
IMPORT
SIG · NPLUSONE
N
nplusone
databasepythonv1.0.0
Install
1.6s avg
Import
45ms
Disk
16MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
Install & Compatibility
Where this runs
tested against v1.0.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.103.920 runs
installs and imports cleanly · install 0.0s · import 0.046s · 18.1MB
glibc
py 3.103.920 runs
installs and imports cleanly · install 1.6s · import 0.044s · 19MB
16MB installed
● package 16MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

Profiler
from nplusone.core.profiler import Profiler
Used for generic profiling outside of framework integrations.
NPlusOneMiddleware
from nplusone.ext.django import NPlusOneMiddleware
Used for Django framework integration.
ext.sqlalchemy
import nplusone.ext.sqlalchemy
Initializes SQLAlchemy integration within the profiler.

This quickstart demonstrates how to use `nplusone` with SQLAlchemy. It sets up a simple ORM model, adds some data, and then uses the `Profiler` context manager to detect N+1 queries when accessing related `Artist` objects within a loop without eager loading. Warnings will be logged to the console.

import logging from nplusone.core.profiler import Profiler import nplusone.ext.sqlalchemy # Configure a logger to capture nplusone warnings logger = logging.getLogger('nplusone') logger.setLevel(logging.WARN) handler = logging.StreamHandler() logger.addHandler(handler) # --- Simulate an SQLAlchemy setup --- from sqlalchemy import create_engine, Column, Integer, String, ForeignKey from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Artist(Base): __tablename__ = 'artists' id = Column(Integer, primary_key=True) name = Column(String) songs = relationship('Song', back_populates='artist') class Song(Base): __tablename__ = 'songs' id = Column(Integer, primary_key=True) title = Column(String) artist_id = Column(Integer, ForeignKey('artists.id')) artist = relationship('Artist', back_populates='songs') engine = create_engine('sqlite:///:memory:') Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() # Add some data artist1 = Artist(name='Artist One') artist2 = Artist(name='Artist Two') session.add_all([artist1, artist2]) session.commit() song1 = Song(title='Song A', artist=artist1) song2 = Song(title='Song B', artist=artist1) song3 = Song(title='Song C', artist=artist2) session.add_all([song1, song2, song3]) session.commit() # --- Nplusone profiling --- print('--- Starting nplusone profiling ---') with Profiler(): songs = session.query(Song).all() print(f'Fetched {len(songs)} songs.') # This will trigger N+1 queries if artists are not eagerly loaded for song in songs: print(f' Song: {song.title}, Artist: {song.artist.name}') print('--- Profiling complete ---') # Example of how to raise an NPlusOneError for tests (optional) # from nplusone.core.exceptions import NPlusOneError # import os # os.environ['NPLUSONE_RAISE'] = 'True' # Set this environment variable or config option # try: # with Profiler(): # songs = session.query(Song).all() # for song in songs: # _ = song.artist.name # except NPlusOneError as e: # print(f'Caught expected NPlusOneError: {e}') # os.environ['NPLUSONE_RAISE'] = '' # Reset for other tests
Debug
Known issues
gotchaNplusone is intended for development and testing environments only. It should NOT be deployed to production environments as it can introduce performance overhead.
fix
Ensure nplusone is only included in your development or test dependencies and configurations (e.g., `requirements-dev.txt`, `DEBUG=True` blocks in Django settings).
affects: All versions
gotchaBy default, nplusone logs warnings. To make it raise an `NPlusOneError` (useful for failing tests), you need to set the `NPLUSONE_RAISE` configuration option (e.g., via an environment variable or Django settings).
fix
Set `NPLUSONE_RAISE = True` in your application's configuration or as an environment variable (e.g., `os.environ['NPLUSONE_RAISE'] = 'True'`). You can also specify the exception type using `NPLUSONE_ERROR`.
affects: All versions
gotchaWhen integrating with specific frameworks like Django or Flask-SQLAlchemy, remember to add the relevant middleware (`NPlusOneMiddleware`) and configure `INSTALLED_APPS` (for Django) to enable automatic detection within the request-response cycle.
fix
For Django, add `'nplusone.ext.django'` to `INSTALLED_APPS` and `'nplusone.ext.django.NPlusOneMiddleware'` to `MIDDLEWARE`. For generic WSGI apps or outside HTTP requests, use the `Profiler` context manager and import the relevant ORM extension (e.g., `import nplusone.ext.sqlalchemy`).
affects: All versions
Errors
Common errors & fixes
NPlusOneError: Potential n+1 query detected on `<model>.<field>`
The `nplusone` library detected an N+1 query problem, and the `NPLUSONE_RAISE` configuration option was set to `True`, causing it to raise an exception instead of just logging a warning.
fix
Optimize your ORM queries using `select_related()` or `prefetch_related()` (for Django) or `joinedload()` or `subqueryload()` (for SQLAlchemy) to eager load related data. Alternatively, set `NPLUSONE_RAISE = False` in your settings to revert to logging warnings, or adjust `NPLUSONE_LOG_LEVEL` to a less severe level if exceptions are not desired.
RecursionError: maximum recursion depth exceeded
This error typically occurs when `nplusone` interacts with complex ORM relationships, specific middleware (like `rest_framework.authtoken`), or deeply nested data structures, leading to an infinite or excessively deep recursive call stack within the library's monitoring mechanisms.
fix
This often points to a bug or limitation within `nplusone` for specific complex scenarios. Possible workarounds include whitelisting the problematic code path using `NPLUSONE_WHITELIST` or temporarily disabling `nplusone` for the part of the application that triggers the recursion. Simplifying the ORM query or relationship structure in the affected area might also resolve it.
ModuleNotFoundError: No module named 'nplusone.ext.django' (or 'nplusone.ext.sqlalchemy')
The framework-specific extension for `nplusone` (e.g., for Django or SQLAlchemy) has not been correctly included in your application's configuration or imported.
fix
For Django, add `'nplusone.ext.django'` to your `INSTALLED_APPS` and `'nplusone.ext.django.NPlusOneMiddleware'` to your `MIDDLEWARE` setting in `settings.py`. For SQLAlchemy or generic WSGI applications, ensure you explicitly import the relevant extension (e.g., `import nplusone.ext.sqlalchemy`) when using the `Profiler` context manager.
Upgrade
Version history
1.0.0latest on PyPI · released May 21, 2018
Audit
Dependencies
DjangooptionalRequired for Django ORM integration if using Django.
SQLAlchemyoptionalRequired for SQLAlchemy ORM integration if using SQLAlchemy.
PeeweeoptionalRequired for Peewee ORM integration if using Peewee.
Agent activity
18 hits · last 30 days
node
14
OpenAI (training)
2
Resources
nplusone — pip install nplusone · libregistry