Are you the author? Sign in to claim
Live sidebar, ER diagrams, CLI, and MCP server for Django models. VS Code + PyPI + MCP Registry + Glama. Zero-dep parser
English · Русский · Español · 中文
Every app. Every model. Every field. Every relationship. Grouped, navigable, and one keystroke away from a live ER diagram.
Django ORM Lens ships as three distributions on one core — pick the one that matches your workflow. Each takes under 60 seconds.
Editor user (VS Code / Cursor / Windsurf / VSCodium / code-server / Gitpod): install the extension → open any Django project → sidebar tree + ER diagram appear.
code --install-extension frowningdev.django-orm-lens # VS Code, Cursor, Windsurf
codium --install-extension frowningdev.django-orm-lens # VSCodium, code-server (via Open VSX)
Terminal / CI user: install from PyPI → run django-orm-lens in any directory that contains Django apps.
pip install django-orm-lens
django-orm-lens # welcome + commands
django-orm-lens scan # scan cwd for apps and models
AI coding agent user (Cursor / Aider / Continue / Zed): install with MCP extras → add one JSON block to your client config.
pip install "django-orm-lens[mcp]"
Then the MCP config snippet in the Integrations section below. Point DJANGO_ORM_LENS_ROOT at your Django project's absolute path.
If the tool saves you a
grepnext time you touch a strange Django project — a star helps others find it.
VS Code / Cursor / Windsurf (VS Code Marketplace):
code --install-extension frowningdev.django-orm-lens
VSCodium / code-server / Gitpod / any OSS Code fork (Open VSX):
codium --install-extension frowningdev.django-orm-lens
Or search Django ORM Lens in the Extensions view — same publisher frowningdev on both registries.
Terminal & AI coding agents:
pip install django-orm-lens # CLI only
pip install "django-orm-lens[mcp]" # + MCP server for AI agents
Requires Python 3.9+. Zero runtime dependencies for the CLI.
Docker (v0.6+):
docker run --rm -v "$PWD:/workspace" ghcr.io/frowningdev/django-orm-lens scan --path .
Multi-arch (amd64 + arm64). No Python required on the host. Good for CI and one-off audits.
Works offline. Works on a broken venv. Works on someone else's laptop. Works in CI.
You open a Django project. It has 20 apps. You need to answer a simple question:
"Which app owns the
Ordermodel, and how is it connected toUser?"
Today, that means: Ctrl+P, "models", scroll through 30 hits, open five files, Ctrl+F for class Order, read through 400 lines of ForeignKey('otherapp.Something') strings, try to remember what you learned two files ago.
Half a day gone. Every time. On every project.
📚 A tree of everythingEvery app → every model → every field → every Icons distinguish |
🕸️ A live ER diagramOne command opens a Mermaid entity-relationship diagram of your entire schema. Watch it redraw as you edit. Export to SVG.
|
🔎 Hover for relationsHover over |
🧭 Jump-to-definitionClick any field in the tree → cursor lands on the exact line. Filter the tree by app or model name. Split |
⚡ Zero configurationNo |
🎨 Native VS Code UIDark theme. Light theme. Your theme. Follows your icon theme, your font, your key bindings. Nothing garish, nothing branded. |
🎯 Inline QuickFixes (16 rules)Static analysis over Suppress inline with |
🧪 Factory generatorRight-click any model → Also available as CodeLens above each model class. |
🕰 Time-Travel Schema DiffPick a Renames are first-class events, never |
🔎 Impact analysis"What breaks if I remove this field?" — right-click a field or model → workspace-wide scan grouped by Django layer (models, serializers, forms, admin, views, urls, templates, tests, migrations). Findings carry a Certain / Likely / Possibly confidence tag. Handles ORM string refs ( |
⚡ Interactive query builderRight-click a field or model → pick a template → snippet inserted at cursor (with tab-stops) or in a fresh untitled buffer.
|
🎨 Sidebar UX overhaulStable
|
Also included in the extension:
CASCADE, through Model, as related_name), theme-aware, one-click SVG exportForeignKey('app.Model') or ManyToManyField(...), with a one-click jump linkclass Model line: field count, relation count, and an Open ER diagram actionauto / default / dark / forest / neutral for the diagram webviewThe same parser that powers the VS Code extension ships as a standalone Python package — with an optional MCP (Model Context Protocol) server so any MCP-compatible AI agent can navigate your Django schema without importing Django or booting your app.
django-orm-lens scan -f json # every app, every model, every field
django-orm-lens describe blog.Post # one model in Markdown
django-orm-lens hover blog.Post # compact hover card
django-orm-lens list | fzf # flat app.Model — pipes anywhere
django-orm-lens er > schema.mmd # Mermaid ER diagram
django-orm-lens diff before.json after.json # what a PR changes structurally
django-orm-lens nplusone --path . # static N+1 detector (v0.6+)
django-orm-lens migration-risk --path . # flag risky migration ops (v0.6+)
Every command accepts --path <dir> and --exclude <glob>. nplusone / migration-risk / diff exit code 1 on findings — drop them into CI to block PRs on regressions.
Register it once with your agent and it exposes five read-only tools:
| 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 |
er_diagram | Mermaid erDiagram for the whole workspace |
# Start it directly
django-orm-lens-mcp
# Or via the CLI subcommand
django-orm-lens mcp
Workspace resolution (py-1.3.0+). Every tool accepts an optional
workspace_root argument on the call. Resolution priority: explicit arg →
$DJANGO_ORM_LENS_ROOT → current working directory. Invalid or non-Django
paths return a structured envelope
({"error": "WORKSPACE_NOT_DJANGO", "hint": "…"}) instead of empty results,
so the agent can self-correct. Optional sandbox via
DJANGO_ORM_LENS_ALLOWED_ROOTS (;-separated on Windows, : elsewhere).
| Client | How to enable | Status |
|---|---|---|
| VS Code | code --install-extension frowningdev.django-orm-lens | ✅ |
| Cursor | same VSIX + optional MCP entry in ~/.cursor/mcp.json | ✅ |
| Windsurf / VSCodium / any Code fork | install the VSIX from the Marketplace or GitHub Releases | ✅ |
| Aider | add django-orm-lens-mcp to your mcp.json | ✅ (via MCP) |
| Continue.dev | register the MCP server in ~/.continue/config.json | ✅ (via MCP) |
| Zed | register the MCP server in Zed settings | ✅ (via MCP) |
| Any MCP-compatible client | point command at django-orm-lens-mcp, set DJANGO_ORM_LENS_ROOT | ✅ |
| Discoverable via MCP Registry | official Model Context Protocol server directory | ✅ |
| Plain terminal / CI | pip install django-orm-lens && django-orm-lens scan | ✅ |
{
"mcpServers": {
"django-orm-lens": {
"command": "django-orm-lens-mcp",
"env": { "DJANGO_ORM_LENS_ROOT": "/abs/path/to/your/project" }
}
}
}
In VS Code:
code --install-extension frowningdev.django-orm-lensmanage.py or models.pyIn a terminal:
pip install django-orm-lens
cd my-django-project
django-orm-lens scan -f table
As an AI agent tool:
pip install "django-orm-lens[mcp]"
…then register django-orm-lens-mcp in your agent's MCP config (see the Integrations table above).
No settings screen. No sign-in. No telemetry.
models.py sprawl.related_name?") without importing the project.runserver, no manage.py migrate, still works.Django ORM Lens sits at the intersection of editor tooling and AI-agent tooling — a slot no existing package covers:
| Segment | Existing option | What it costs you |
|---|---|---|
| Boot-and-graph | django-extensions graph_models | Requires Graphviz + Django settings + a working DB URL |
| Web-based viewer | django-schema-graph | Requires a running Django server; hosts one more thing to break |
| Admin panel | Django Admin | Requires runserver + auth + database — great for data, not for architecture |
| Editor plugin | PyCharm's Django Structure | Locked to PyCharm; no CLI, no AI-agent story |
| MCP server | (none until now) | AI agents guess your schema from source, imperfectly |
Django ORM Lens is the only tool that ships three surfaces from one parser: a VS Code extension (any Code fork), a zero-dep CLI (terminals + CI), and an MCP server (AI agents). All static. All free. All MIT.
| Django ORM Lens | django-extensions graph_models | django-schema-graph | Django Admin | PyCharm Django Structure | |
|---|---|---|---|---|---|
| Works without a bootable Django project | ✅ | ❌ | ❌ | ❌ | ⚠️ |
| Zero-install (no graphviz, no server) | ✅ | ❌ | ❌ | ❌ | ❌ (needs PyCharm) |
| Works in VS Code / Cursor / any Code fork | ✅ | ❌ | ❌ | ❌ | ❌ |
| Sidebar tree inside the editor | ✅ | ❌ | ❌ | ❌ | ✅ |
| Live ER diagram | ✅ | ✅ | ✅ | ❌ | ❌ |
Hover cards on ForeignKey | ✅ | ❌ | ❌ | ❌ | ⚠️ |
| CodeLens on model classes | ✅ | ❌ | ❌ | ❌ | ❌ |
Split models/ package support | ✅ | ⚠️ | ⚠️ | ✅ | ✅ |
| CLI for terminal / CI | ✅ | ⚠️ | ❌ | ❌ | ❌ |
| MCP server for AI agents | ✅ | ❌ | ❌ | ❌ | ❌ |
| Discoverable in the MCP Registry | ✅ | ❌ | ❌ | ❌ | ❌ |
| Free & open-source (MIT) | ✅ | ✅ | ✅ | ✅ | ❌ (paid IDE) |
| Django version support | 4.0 – 5.2 | latest | 3.2 – 4.1 (stale since 2023) | latest | latest |
django-schema-graphhas not been updated since 2023-05 and does not test Django 5.x.
The defaults are opinionated and sensible. If you need to tweak:
// .vscode/settings.json
{
"djangoOrmLens.excludeGlobs": [
"**/migrations/**",
"**/node_modules/**",
"**/venv/**",
"**/.venv/**",
"**/env/**"
],
"djangoOrmLens.autoRefresh": true
}
| Setting | Type | Default | What it does |
|---|---|---|---|
djangoOrmLens.excludeGlobs | string[] | See above | Glob patterns to skip when scanning |
djangoOrmLens.autoRefresh | boolean | true | Rescan on models.py changes |
djangoOrmLens.codeFixes.enabled | boolean | true | Master switch for the DOL### diagnostics + QuickFixes |
djangoOrmLens.rules | object | {} | Per-rule severity: { "DOL007": "off", "DOL013": "error" } |
djangoOrmLens.rulesSelect | string[] | [] | Ruff-style select. ["DOL0"] runs only queryset+model rules |
djangoOrmLens.rulesIgnore | string[] | [] | Ruff-style ignore. ["DOL03"] silences form/view rules |
Sixteen static-analysis checks over .py files. All are line-oriented (no Python process required), grouped into four categories, and individually toggle-able. Every rule has a stable DOL### code that appears in the Problems panel and links to its docs.
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL001 | .count() > 0 → .exists() | info | safe |
DOL002 | .count() == 0 → not .exists() | info | safe |
DOL003 | .first() is None → not .exists() | info | safe |
DOL004 | .first() is not None → .exists() | info | safe |
DOL005 | .filter().exclude() → Q(...) & ~Q(...) | hint | suggestion |
DOL006 | for x in list(qs): → for x in qs: | info | safe |
DOL007 | FK attribute access inside for-loop (N+1) | warning | unsafe |
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL011 | CharField / TextField(null=True) → blank=True | warning | suggestion |
DOL012 | Model without __str__ method | info | suggestion |
DOL013 | ForeignKey without on_delete | error | suggestion |
DOL014 | CharField without max_length | error | suggestion |
DOL015 | TextField(max_length=...) has no DB effect | hint | suggestion |
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL021 | datetime.now() → timezone.now() | warning | suggestion |
DOL022 | datetime.utcnow() (deprecated 3.12) | warning | suggestion |
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL031 | render(request, tpl, locals()) — explicit dict | warning | suggestion |
DOL032 | Meta.fields = '__all__' — audit surface | warning | unsafe |
# django-orm-lens-disable-next-line DOL007
for user in User.objects.all():
print(user.profile) # not flagged
qs.count() > 0 # django-orm-lens-disable-line DOL001
# django-orm-lens-disable DOL011 ← on its own line, kills DOL011 for the rest of the file
Applicability follows Rust's Clippy: safe fixes can be applied automatically ("Fix All"), suggestion fixes are offered as a QuickFix but reviewed, unsafe findings never auto-apply. Fixes are separated from analyzers (Roslyn-style), so one rule can grow multiple fixers over time without touching detection logic.
Open the command palette (Ctrl+Shift+P / Cmd+Shift+P) and type "Django ORM Lens":
| Command | What it does |
|---|---|
Django ORM Lens: Refresh | Force-rescan the workspace |
Django ORM Lens: Show ER Diagram | Open the Mermaid ER diagram side-by-side |
Django ORM Lens: Filter Models | Filter the tree by app / model / field name |
Django ORM Lens: Clear Filter | Restore the full tree |
Django ORM Lens: Jump to Model | Programmatic — triggered by tree clicks and hover cards |
Django ORM Lens: Find Reverse References | Right-click a model — QuickPick of every FK pointing at it |
Django ORM Lens: Generate factory_boy Factory | Right-click a model or use CodeLens — scaffold a DjangoModelFactory |
Django ORM Lens: Schema Diff (Time-Travel) | Pick two commits — get a typed diff as a markdown buffer |
Django ORM Lens: Find Impact (What Uses This?) | Right-click a field or model — workspace-wide reference scan |
Django ORM Lens: Build Query (Insert Snippet) | Right-click a field or model — pick an ORM template |
Shipped
ForeignKey('app.Model')models/ package supportN fields · N relations · Open ER diagram)CASCADE, SET_NULL, PROTECT, related_name)auto / default / dark / forest / neutral)through_model on M2M edges (contributed by @kingrubic)nplusone — static N+1 detector (FK/M2M access inside loops without select_related/prefetch_related)migration-risk — flags 7 classes of production hazards in migrations/*.pydiff — compare two schema JSON dumps for PR reviewdocker run ghcr.io/frowningdev/django-orm-lenssettings.AUTH_USER_MODEL resolves everywhere: n+1 reverse-relations, signal senders, Mermaid ER, VS Code webview, inbound-relation panel, React ERForeignKey(on_delete=CASCADE, to='User') resolves regardless of kwarg order (Python + TS parity)find_user_model, resolve_related_tail, find_model, iter_workspace_py_files (Python) + findUserModel, resolveRelatedTail (TS)--verbose no longer walks the tree twice; WorkspaceIndex.scanned_files carries the countjti: CharField[str] = models.CharField(...)) now parse — reported by @jsabater (#25) with a clean Django Ninja 1.6 reproclass Container[T](models.Model): now parsesfrom django.db import models as m) and third-party field packages (jsonfield.JSONField) now detectedDOL001..DOL032) with per-rule severity + Ruff-style select/ignore + inline # django-orm-lens-disable-next-linefactory_boy scaffold from any model with Faker providers keyed by field type.select_related, related_name honoured)TreeItem.id, MarkdownString tooltips with command: deep-links, FileDecorationProvider badges, TreeView.badge on the activity bar, three when-gated viewsWelcome statesNext
.filter() / .exclude() / .annotate() (#3)migration-risk + nplusoneLater
django-mptt, django-taggit, django-model-utils)Vote by 👍-ing the corresponding issue.
models/ package. Does that work?models/*.py alongside classic models.py.
models.Model, abstract bases starting with Abstract, common mixins ending in Mixin, and known base names like TimeStampedModel or PolymorphicModel. Non-model classes (ModelAdmin, ModelSerializer, Form, View, Manager, …) are filtered out.
command at the installed django-orm-lens-mcp binary. See the Integrations section.
models.py snippet)MIT © FROWNINGdev
Made for developers who care about their codebase.
Marketplace · PyPI · GitHub · Issues · Discussions · Sponsor
Official MongoDB integration — query collections, run aggregations, inspect schemas
Secure MCP server for MySQL database interaction, queries, and schema management
Run analytics queries on ClickHouse — explore schemas, execute SQL, fetch results
Run Claude Code as an MCP server so any agent can delegate coding tasks to it