DEV Community

atiqur rahman
atiqur rahman

Posted on

How to Stop N+1 Database Queries in Django Tests with django-query-guard

N+1 database queries and silent query count growth can severely affect Django application performance. Unfortunately, they are often discovered too lateβ€”in production server logs or APM dashboards.

To solve this problem, I built django-query-guardβ€”an open-source Pytest plugin that brings database performance checks directly into automated test suites.


πŸ’‘ The Problem: Silent N+1 Queries

In Django, the ORM makes fetching related models effortless, but it’s terrifyingly easy to write an accidental N+1 loop:


python
# ❌ N+1 Query Accident
# Fetches 100 users (1 query), then executes 100 individual queries for each profile!
# Total: 101 Database Queries! 🐌
users = User.objects.all()
profiles = [user.profile.bio for user in users]

πŸ›‘οΈ The Solution: django-query-guard
Instead of relying on manual code reviews, django-query-guard turns query limits and N+1 prevention into enforceable Pytest assertions:

python


import pytest
@pytest.mark.django_db
@pytest.mark.query_guard(max_queries=2, detect_n_plus_one=True)
def test_user_profiles_api(client):
    response = client.get("/api/users/")
    assert response.status_code == 200
If your endpoint accidentally executes 101 queries instead of 2, Pytest fails instantly with an exact breakdown of which SQL statement repeated! πŸ’₯

πŸ”₯ Key Features
🎯 Pytest Marker Integration: @pytest.mark.query_guard(max_queries=N)
🧠 Smart SQL Normalization: Parameter-variation filtering prevents false positives on intentional duplicate queries with identical parameters.
πŸ—„οΈ Multi-Database Support: Monitors queries across all configured Django database connections (default, replica, etc.).
πŸ“Š HTML Query Reports: Dark-themed HTML report generation after test runs (pytest --query-guard-report=report.html).
πŸ“ˆ Trend History Tracking: JSON-based run history with regression detection across test builds (pytest --query-guard-trend=.query_guard_history.json).
🐍 Wide Compatibility: Fully tested on Python 3.10–3.14 and Django 4.0–6.0.
πŸ“¦ Installation & Setup
Install the stable package from PyPI:

bash


pip install django-query-guard
Or with development tools:

bash


pip install django-query-guard[dev]
πŸ“Š Generating Visual HTML Reports
You can generate a dark-themed HTML query report after any test run:

bash


pytest --query-guard-report=report.html
The report includes:

Summary metrics (Total tests, pass/fail counts, total queries, N+1 detections)
Per-test query count breakdown
Detailed SQL query execution duration and parameters for failing tests

Enter fullscreen mode Exit fullscreen mode

Top comments (0)