Static ORM query analyzer for Django — detects N+1, missing indexes, unused queries and more
Project description
django-fox-query 🦊
Static ORM query analyzer for Django. Scans your codebase and detects query performance issues before they hit production.
Installation
pip install django-fox-query
Add to your INSTALLED_APPS:
INSTALLED_APPS = [
...
"django_fox_query",
]
Usage
python manage.py find_query
That's it. No configuration needed, no runtime overhead — it's pure static analysis.
What it detects
N+1 queries
Detects querysets on models that have ForeignKey, OneToOneField or ManyToManyField relations without select_related() or prefetch_related().
# ⚠️ detected
books = Book.objects.all()
# ✅ ok
books = Book.objects.all().select_related('author')
Missing indexes on filter()
Cross-references the fields used in .filter() and .exclude() with the model definition to detect fields without db_index=True or unique=True.
# ⚠️ detected — title has no index
books = Book.objects.filter(title="Django")
Unnecessary field loading
Tracks which fields are actually accessed on a queryset and suggests .values() or .values_list() when only a subset is used.
# ⚠️ detected — only author__name is used
books = Book.objects.all()
for book in books:
print(book.author.name)
# ✅ suggestion
books = Book.objects.values('author__name')
Unused querysets
Detects querysets assigned to a variable that is never used in the scope.
# 🗑️ detected — authors is never used
authors = Author.objects.filter(name="Victor Hugo")
len() instead of .count()
Detects len(queryset) calls which load all objects into memory instead of issuing a single COUNT query.
# ⚡ detected
count = len(Book.objects.all())
# ✅ suggestion
count = Book.objects.count()
Example output
📄 /app/core/views.py:4
scope → my_view
model → Book
method → .objects.all()
code → books = Book.objects.all()
⚠️ warning → relation 'author' (ForeignKey) → utilise .select_related('author')
💡 values → seuls 'author__name' sont utilisés → envisage .values('author__name')
📄 /app/core/views.py:5
scope → my_view
model → Author
method → .objects.filter()
code → authors = Author.objects.filter(name="Victor Hugo")
🗑️ unused → 'authors' n'est jamais utilisé → query inutile, envisage la suppression
🔍 filter → filter sur 'name' sans index → ajoute db_index=True sur Author.name
── len() vs count() ──────────────────
📄 /app/core/views.py:12
code → count = len(books)
⚡ count → len(books) sur un queryset → utilise books.count()
⚠️ 3 N+1 potentiel(s) détecté(s)
4 queries trouvées
How it works
find_query parses your Python files using the ast module — no code is executed, no database connection is needed. It builds a map of your ORM calls and your model definitions, then cross-references the two to surface issues.
Migrations and site-packages are automatically ignored.
Requirements
- Python 3.10+
- Django 4.0+
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_fox_query-0.1.0.tar.gz.
File metadata
- Download URL: django_fox_query-0.1.0.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
227329946502ac6a6d5b54c59e9662c713b1e894c90e42dd11a20b5fcaab74ce
|
|
| MD5 |
b7df3613e099b10f0c8f2ed4902254de
|
|
| BLAKE2b-256 |
34327ddcb47842dabc28860aee11e7911e06ec577562c6d9da56a6881e70b075
|
File details
Details for the file django_fox_query-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_fox_query-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fc2a4c1061b73ee5cdbaac38673b81e52d93dc75433e145bc0f09036e94c231
|
|
| MD5 |
d7d32d124015f13f5715b1af77f18c6e
|
|
| BLAKE2b-256 |
8758d200ad19914b4bf9f816239311c7e30d24f1b2ad720494267ea931001e0d
|