English · Русский · Español · 中文
Django ORM Lens
See your entire Django schema — in your editor, in your terminal, and from your AI agent.
Every app. Every model. Every field. Every relationship. Grouped, navigable, and one keystroke away from a live ER diagram.
🎯 Pick your path
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.
📊 Traction
If the tool saves you a
grepnext time you touch a strange Django project — a star helps others find it.
📈 Star growth
⚡ Install
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.
🎯 The problem
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.
✨ With Django ORM Lens
📚 A tree of everything
Every app → every model → every field → every Meta option. Grouped by application, sorted alphabetically, expandable.
Icons distinguish CharField from ForeignKey from ManyToManyField at a glance.
🕸️ A live ER diagram
One command opens a Mermaid entity-relationship diagram of your entire schema. Watch it redraw as you edit. Export to SVG.
ForeignKey, OneToOneField, and ManyToManyField become proper cardinality arrows.
🔎 Hover for relations
Hover over ForeignKey('app.Model') in any Python file → a card pops up with the target model's fields, relations, and a "Jump to" link. No Ctrl+F, no file dialog.
🧭 Jump-to-definition
Click any field in the tree → cursor lands on the exact line. Filter the tree by app or model name. Split models/ packages are fully supported.
⚡ Zero configuration
No DJANGO_SETTINGS_MODULE. No runserver. Parses models.py statically. Works with a broken venv, a missing dependency, or on someone else's laptop.
🎨 Native VS Code UI
Dark theme. Light theme. Your theme. Follows your icon theme, your font, your key bindings. Nothing garish, nothing branded.
🚀 v0.8 — five new "wow" features
🎯 Inline QuickFixes (16 rules)
Static analysis over .py files with Ruff-style codes (DOL001..DOL032), Clippy-style Applicability, and per-rule severity overrides. .count() > 0 → .exists(), null=True on CharField, missing on_delete, datetime.now() → timezone.now() and a dozen more.
Suppress inline with # django-orm-lens-disable-next-line DOL007.
🧪 Factory generator
Right-click any model → factory_boy DjangoModelFactory scaffold with Faker providers keyed by field type. CharField(max_length) scales word-count buckets, DecimalField(N,D) computes left_digits=N-D, choices= maps to Iterator, M2M gets @post_generation. FK chains pull related factories transitively.
Also available as CodeLens above each model class.
🕰 Time-Travel Schema Diff
Pick a models.py, pick two commits, get a typed diff as PR-ready markdown. AddModel / DropModel / RenameModel / ModifyModel events with confidence-scored rename detection (Levenshtein + field-shape Jaccard).
Renames are first-class events, never Add + Drop. Blob-SHA LRU cache — commits that don't touch models.py share their parsed snapshot.
🔎 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 (order_by("-author")), kwarg lookups (filter(author__id=1)), Meta.fields tuples, and template variables.
⚡ Interactive query builder
Right-click a field or model → pick a template → snippet inserted at cursor (with tab-stops) or in a fresh untitled buffer.
.filter(field=?) on an FK auto-appends .select_related(...), .annotate(post_count=Count('post_set')) honours related_name, .prefetch_related for M2M, .values('field').distinct(), .only('field').
🎨 Sidebar UX overhaul
Stable TreeItem.id — refresh no longer collapses the tree. Rich MarkdownString tooltips with command: deep-links. Activity-bar badge counts DOL### issues.
FileDecorationProvider badges: red ! on FK-without-on_delete, yellow ~ on null=True string fields (bubbles up to the parent Model row, Git-style).
📸 What it looks like
Also included in the extension:
- 🕸️ Live ER diagram — Mermaid cardinality arrows, edge labels (
CASCADE,through Model,as related_name), theme-aware, one-click SVG export - 🔎 Hover cards — over any
ForeignKey('app.Model')orManyToManyField(...), with a one-click jump link - 🧭 CodeLens — above every
class Modelline: field count, relation count, and an Open ER diagram action - 🎨 Named themes —
auto/default/dark/forest/neutralfor the diagram webview
🤖 For terminals and AI coding agents
The 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.
CLI
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.
MCP server
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).
🔌 Integrations
| 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 |
✅ |
Example: Cursor / any MCP client
{
"mcpServers": {
"django-orm-lens": {
"command": "django-orm-lens-mcp",
"env": { "DJANGO_ORM_LENS_ROOT": "/abs/path/to/your/project" }
}
}
}
🚀 Get started (30 seconds)
In VS Code:
code --install-extension frowningdev.django-orm-lens- Open a folder with a
manage.pyormodels.py - Click the Django ORM Lens icon in the activity bar
- Expand apps → models → fields
- Click the type-hierarchy icon at the top of the panel → ER diagram opens beside your code
In 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.
🎯 Who this is for
- Django developers joining a codebase with 10+ apps and getting lost in
models.pysprawl. - Contract / freelance engineers who need to grasp an unfamiliar Django project in the first hour, not the first week.
- Teams onboarding new hires who want a one-glance schema view without spinning up documentation infrastructure.
- AI-agent power users (Cursor / Aider / Zed / Continue / any MCP-compatible client) who need the agent to answer schema questions accurately — without giving it database credentials or booting Django.
- CI pipelines that verify schema shape (e.g. "did we accidentally break a
related_name?") without importing the project. - Solo indie devs on a broken venv or someone else's laptop — no
runserver, nomanage.py migrate, still works.
🗺️ Market position
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.
🤔 How is this different?
| 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.
⚙️ Configuration
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 |
🔬 Rule catalogue (v0.8)
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.
Queryset
| 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 |
Model definition
| 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 |
Datetime
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL021 |
datetime.now() → timezone.now() |
warning | suggestion |
DOL022 |
datetime.utcnow() (deprecated 3.12) |
warning | suggestion |
Forms / views
| Code | Rule | Default | Applicability |
|---|---|---|---|
DOL031 |
render(request, tpl, locals()) — explicit dict |
warning | suggestion |
DOL032 |
Meta.fields = '__all__' — audit surface |
warning | unsafe |
Suppress inline
# 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.
🧭 Commands
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 |
🗺️ Roadmap
Shipped
- Sidebar tree grouped by app
- Live Mermaid ER diagram
- Hover cards over
ForeignKey('app.Model') - Filter tree by name
- Split
models/package support - Export ER diagram as SVG
- Python CLI + MCP server for terminals and AI agents
- Welcome view for empty workspaces
- Path-safe jump-to-definition and sanitized hover markdown
- v0.3.0 — CodeLens above each model class (
N fields · N relations · Open ER diagram) - v0.3.0 — Edge labels on the diagram (
CASCADE,SET_NULL,PROTECT,related_name) - v0.3.0 — Named color themes (
auto/default/dark/forest/neutral) - v0.3.1 —
through_modelon M2M edges (contributed by @kingrubic) - v0.3.1 — Listed in the official MCP Registry + Glama.ai
- v0.6.0 — CLI
nplusone— static N+1 detector (FK/M2M access inside loops withoutselect_related/prefetch_related) - v0.6.0 — CLI
migration-risk— flags 7 classes of production hazards inmigrations/*.py - v0.6.0 — CLI
diff— compare two schema JSON dumps for PR review - v0.6.0 — ER-diagram minimap color-codes nodes by Django app
- v0.6.0 — README translations: 🇷🇺 Russian, 🇪🇸 Spanish, 🇨🇳 Chinese
- v0.6.0 — Docker image on GHCR:
docker run ghcr.io/frowningdev/django-orm-lens - v0.7.0 —
settings.AUTH_USER_MODELresolves everywhere: n+1 reverse-relations, signal senders, Mermaid ER, VS Code webview, inbound-relation panel, React ER - v0.7.0 — AST-based field parser:
ForeignKey(on_delete=CASCADE, to='User')resolves regardless of kwarg order (Python + TS parity) - v0.7.0 — Public shared helpers:
find_user_model,resolve_related_tail,find_model,iter_workspace_py_files(Python) +findUserModel,resolveRelatedTail(TS) - v0.7.0 —
--verboseno longer walks the tree twice;WorkspaceIndex.scanned_filescarries the count - v0.7.3 — PEP-526 type annotations on fields (
jti: CharField[str] = models.CharField(...)) now parse — reported by @jsabater (#25) with a clean Django Ninja 1.6 repro - v0.7.4 — PEP-695 generic class headers (Python 3.12+):
class Container[T](https://github.com/FROWNINGdev/django-orm-lens/blob/main/models.Model):now parses - v0.7.5 — Aliased models module (
from django.db import models as m) and third-party field packages (jsonfield.JSONField) now detected - v0.7.6 — Tab-indented model bodies now parse (editors defaulting to tabs no longer show empty models)
- v0.8.0 — Inline QuickFixes: 16 rules (
DOL001..DOL032) with per-rule severity + Ruff-style select/ignore + inline# django-orm-lens-disable-next-line - v0.8.0 — Factory generator:
factory_boyscaffold from any model with Faker providers keyed by field type - v0.8.0 — Time-Travel Schema Diff: pick two commits → typed markdown diff with first-class rename detection
- v0.8.0 — Impact analysis: workspace-wide field-reference scan across every Django layer with Certain/Likely/Possibly confidence tags
- v0.8.0 — Interactive Query Builder: right-click → template → snippet inserted at cursor, grammar-aware (FK gets
.select_related,related_namehonoured) - v0.8.0 — Sidebar UX overhaul: stable
TreeItem.id,MarkdownStringtooltips withcommand:deep-links,FileDecorationProviderbadges,TreeView.badgeon the activity bar, three when-gatedviewsWelcomestates
Next
- ORM query autocomplete inside
.filter()/.exclude()/.annotate()(#3) - App / model toggle checkboxes to declutter huge schemas
- Migration dependency graph
- pre-commit hook wrapping
migration-risk+nplusone
Later
- Third-party field support (
django-mptt,django-taggit,django-model-utils) - JetBrains / PyCharm plugin (if there is demand)
Vote by 👍-ing the corresponding issue.
❓ FAQ
🆘 Support
- 🐛 Bug reports — GitHub Issues (please include a minimal
models.pysnippet) - 💡 Feature requests / ideas — GitHub Discussions
- 📝 Marketplace reviews — rate the extension (the fastest signal that keeps this project moving)
- 🐍 PyPI page — pypi.org/project/django-orm-lens
- 💚 Sponsor — github.com/sponsors/FROWNINGdev
📜 License
MIT © FROWNINGdev
Made for developers who care about their codebase.
Marketplace · PyPI · GitHub · Issues · Discussions · Sponsor
No comments yet
Be the first to share your take.