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
muslpy 3.10–3.920 runs
installs and imports cleanly · install 0.0s · import 0.046s · 18.1MB
glibcpy 3.10–3.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
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.
fixOptimize 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.
fixThis 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.
fixFor 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.