Static analysis + MCP server for Django models. Blast radius, schema drift, N+1 and migration risk for CI, ER diagrams for terminals and AI agents. No DB, no Django boot. Free and MIT.
Project description
django-orm-lens
Static analysis + MCP server for Django models. Terminal- and AI-agent-friendly.
Listed in the official MCP Registry as io.github.FROWNINGdev/django-orm-lens.
Ships with a zero-dependency parser, a JSON/Markdown/table CLI, and an optional MCP (Model Context Protocol) server so any AI coding agent — Cursor, Aider, Continue, and any other MCP client — can navigate your Django schema without importing Django or spinning up your app.
Companion to the Django ORM Lens VS Code extension.
Paid-tier capabilities, free and MIT
Schema review is a paid category nearly everywhere. A bot that reviews every pull request, analysis that follows a queryset past the function it was built in, a check that catches schema drift, index advice grounded in real table statistics — those normally sit behind a per-seat or per-database subscription.
All of it is here, MIT-licensed, with no tier gate, no seat count, no account and no telemetry:
| Capability usually sold as a paid tier | Here |
|---|---|
| PR review bot for schema changes — posts once, then updates in place | blast-radius + the GitHub Action |
| Analysis that follows a queryset across functions | nplusone |
| Schema drift detection | drift |
| Index proposals from observed QuerySet usage | suggest-indexes |
| Migration risk weighed against real table sizes | blast-radius --stats |
| Blast radius of a destructive migration | blast-radius |
| Cross-layer impact of removing a field | impact |
There is no Pro tier, and none is planned.
Install
# Core CLI (zero third-party deps)
pip install django-orm-lens
# With the MCP server (adds the `mcp` package)
pip install "django-orm-lens[mcp]"
Requires Python 3.9+. Works on Linux, macOS, and Windows.
CLI usage
# Scan a Django project for models (JSON, Markdown, or table)
django-orm-lens scan -f json
django-orm-lens scan -f markdown
django-orm-lens scan -f table
# Describe one model
django-orm-lens describe blog.Post
django-orm-lens describe Post -f json
# Compact hover card (great for pipeing into your editor)
django-orm-lens hover blog.Post
# Flat list — pipes into fzf, grep, etc.
django-orm-lens list | fzf
# ER diagram — Mermaid (default), DBML, D2, or PlantUML
django-orm-lens er > schema.mmd
django-orm-lens er -f dbml > schema.dbml # paste into dbdiagram.io
django-orm-lens er -f d2 > schema.d2 # render: d2 schema.d2 schema.svg
django-orm-lens er -f plantuml > schema.puml
# Diff two schema dumps (exit 1 on changes — CI-friendly)
django-orm-lens diff before.json after.json
# Static analyzers — text, json, sarif, or github annotation output
django-orm-lens nplusone --format github # N+1 findings as PR annotations
django-orm-lens migration-risk -f sarif # SARIF for GitHub Code Scanning
django-orm-lens suggest-indexes blog.Post # Meta.indexes proposals from usage
django-orm-lens signals # sender→signal→handler graph
django-orm-lens migration-deps blog -f mermaid # per-app migration DAG
django-orm-lens cascade blog.Author # delete blast radius by on_delete
Every command accepts --path <dir> and repeatable --exclude <glob>. Defaults
skip migrations/, venv/, .venv/, env/, and node_modules/.
MCP server — for AI coding agents
The MCP server exposes ten read-only tools that any MCP-compatible agent can call while it edits your Django project:
| Tool | Purpose |
|---|---|
list_apps |
Every Django app in the workspace with model counts |
list_models |
Flat app.Model list, optional app filter |
describe_model |
Full field / relation / Meta detail for one model |
find_relations |
Inbound + outbound relations for one model |
cascade_preview |
Blast radius of one delete(), grouped by on_delete |
er_diagram |
ER diagram — mermaid / dbml / d2 / plantuml |
describe_migration_dependency |
Per-app migration DAG: roots, leaves, cross-app deps |
suggest_indexes |
Meta.indexes proposals from observed QuerySet usage |
signal_graph |
Sender→signal→handler graph from @receiver decorators |
nplusone_scan |
Static N+1 findings for the whole workspace |
Start the server manually
django-orm-lens-mcp # dedicated entry point
# or
django-orm-lens mcp # subcommand
Workspace resolution (py-1.3.0+). Priority: explicit workspace_root
argument on the tool call → DJANGO_ORM_LENS_ROOT env var → current working
directory. If none resolves to a Django project (manage.py, django in
pyproject.toml, or any models.py) you get a structured error envelope
back — {"error": "WORKSPACE_NOT_DJANGO", "hint": "…"} — instead of an
empty list, so the agent knows what to do next.
Optional sandbox: set DJANGO_ORM_LENS_ALLOWED_ROOTS (;-separated on
Windows, :-separated elsewhere) to a whitelist of prefixes; any path
outside them is rejected with WORKSPACE_NOT_ALLOWED.
Register it with an agent
Cursor — add to ~/.cursor/mcp.json:
{
"mcpServers": {
"django-orm-lens": {
"command": "django-orm-lens-mcp",
"env": { "DJANGO_ORM_LENS_ROOT": "/abs/path/to/your/project" }
}
}
}
Any MCP client — same shape, generic tool. Point command at the
installed django-orm-lens-mcp binary. Two ways to tell it which Django
project to scan:
- Set
DJANGO_ORM_LENS_ROOTinenv— the whole session uses one project. Simplest for single-repo workflows. - Pass
workspace_rooton each tool call — the agent switches projects per call. Useful for mono-repos and multi-workspace setups.
The tool signatures include workspace_root: str = "" as an optional
parameter, so any agent inspecting tools/list sees it and can supply it.
CI gates
diff and nplusone exit 1 on findings; migration-risk exits 1 on
critical findings (--exit-zero for report-only). Two annotation-ready
formats: --format github prints ::warning/::error workflow commands
(PR annotations with no extra permissions), --format sarif emits
SARIF 2.1.0 for github/codeql-action/upload-sarif.
pre-commit users get two ready-made hooks:
repos:
- repo: https://github.com/FROWNINGdev/django-orm-lens
rev: py-v1.8.0
hooks:
- id: django-orm-lens-nplusone
- id: django-orm-lens-migration-risk
GitHub Actions users get a composite action:
uses: FROWNINGdev/django-orm-lens@action-v1 with command: / format: inputs.
Why?
Django's ORM is Python, and Python is dynamic. AI agents that only see
models.py as raw text miss:
- which fields belong to which model;
- the direction and cardinality of every relation;
- what
Meta.ordering,unique_together, andconstraintsactually contain; - which app owns which model when the project uses split
models/packages.
django-orm-lens gives them a static, deterministic, JSON view of the
schema — no Django boot, no database, no side effects. And you get a nice CLI
for humans too.
Programmatic API
from django_orm_lens import scan_workspace
index = scan_workspace(".")
for app in index.apps:
for model in app.models:
print(f"{app.name}.{model.name} — {len(model.fields)} fields")
# The full parsed tree serialises to the same JSON schema the VS Code
# extension emits, so tools can share it interchangeably.
import json
json.dumps(index.to_dict(), indent=2)
Links
- Repo: https://github.com/FROWNINGdev/django-orm-lens
- Issues: https://github.com/FROWNINGdev/django-orm-lens/issues
- Changelog: https://github.com/FROWNINGdev/django-orm-lens/blob/main/CHANGELOG.md
- VS Code extension: https://marketplace.visualstudio.com/items?itemName=frowningdev.django-orm-lens
MIT licensed.
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_orm_lens-1.8.0.tar.gz.
File metadata
- Download URL: django_orm_lens-1.8.0.tar.gz
- Upload date:
- Size: 148.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fded12c1a6fe8b2576923afd85e93087b9279cad3d37472b12cd545dfed4da1
|
|
| MD5 |
e66036d3e573958e41957e25204ad17f
|
|
| BLAKE2b-256 |
29803a414a1a92874cef40ed0975dba394b8677308531267ca99991cbf5d8a67
|
Provenance
The following attestation bundles were made for django_orm_lens-1.8.0.tar.gz:
Publisher:
publish-pypi.yml on FROWNINGdev/django-orm-lens
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_orm_lens-1.8.0.tar.gz -
Subject digest:
7fded12c1a6fe8b2576923afd85e93087b9279cad3d37472b12cd545dfed4da1 - Sigstore transparency entry: 2280106251
- Sigstore integration time:
-
Permalink:
FROWNINGdev/django-orm-lens@9e250252bae08092d75b069127ef98d904ec1222 -
Branch / Tag:
refs/tags/py-v1.8.0 - Owner: https://github.com/FROWNINGdev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@9e250252bae08092d75b069127ef98d904ec1222 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_orm_lens-1.8.0-py3-none-any.whl.
File metadata
- Download URL: django_orm_lens-1.8.0-py3-none-any.whl
- Upload date:
- Size: 97.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
936c85b322db12ae3ff2ca0701274b4facf8bbcbeea4fd4c9e293214bb97f33f
|
|
| MD5 |
5ceb64ebe8620e5fb698b8ef6042a623
|
|
| BLAKE2b-256 |
06d02790eebdd96c183b48668ac25789d07a5157a5052fc629d8587351e45955
|
Provenance
The following attestation bundles were made for django_orm_lens-1.8.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on FROWNINGdev/django-orm-lens
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_orm_lens-1.8.0-py3-none-any.whl -
Subject digest:
936c85b322db12ae3ff2ca0701274b4facf8bbcbeea4fd4c9e293214bb97f33f - Sigstore transparency entry: 2280106287
- Sigstore integration time:
-
Permalink:
FROWNINGdev/django-orm-lens@9e250252bae08092d75b069127ef98d904ec1222 -
Branch / Tag:
refs/tags/py-v1.8.0 - Owner: https://github.com/FROWNINGdev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@9e250252bae08092d75b069127ef98d904ec1222 -
Trigger Event:
push
-
Statement type: