**English** · [Русский](https://github.com/FROWNINGdev/django-orm-lens/blob/main/docs/i18n/README.ru.md) · [Español](https://github.com/FROWNINGdev/django-orm-lens/blob/main/docs/i18n/README.es.md) · [中文](https://github.com/FROWNINGdev/django-orm-lens/blob/main/docs/i18n/README.zh.md)
# Django ORM Lens
### ER diagrams, N+1 detection and migration-risk checks for Django — without booting it.
Your entire model graph — live in your editor sidebar, gating your CI, and answering your AI agent over MCP. All from static parsing: no database, no `runserver`, no working venv.
**Replaces:** `graph_models` + `django-schema-graph` + hand-drawn ER diagrams + grep archaeology.
[](https://pypi.org/project/django-orm-lens/)
[](https://pypi.org/project/django-orm-lens/)
[](https://pypi.org/project/django-orm-lens/)
[](https://github.com/FROWNINGdev/django-orm-lens/actions/workflows/ci.yml)
[](https://pepy.tech/project/django-orm-lens)
[](LICENSE)
[](https://marketplace.visualstudio.com/items?itemName=frowningdev.django-orm-lens)
[](https://open-vsx.org/extension/frowningdev/django-orm-lens)
[](https://github.com/FROWNINGdev/django-orm-lens/pkgs/container/django-orm-lens)
Featured in Django News #347 · PyCoder's Weekly #746
---
## ⚡ 10 seconds to first insight
```bash
uvx django-orm-lens scan -f table # every app and model at a glance
uvx django-orm-lens nplusone # N+1 loops, with the select_related to add
uvx django-orm-lens migration-risk # migrations that lock tables or fail on existing rows
```
Cold clone, broken venv, no settings module — you still see every app and model of the project, then the N+1 loops and risky migrations in it, right in your terminal. (`uvx` is from [uv](https://docs.astral.sh/uv/); `pipx run` works the same way.)
**Then pick your surface** — three distributions, one parser core:
| You are | Install | You get |
|---|---|---|
| **Editor user** — VS Code / Cursor / Windsurf / VSCodium | `code --install-extension frowningdev.django-orm-lens` | Field autocomplete in `.filter()`, sidebar tree, live ER diagram, hover cards, 18 lint rules with QuickFixes |
| **Terminal / CI user** | `pip install django-orm-lens` | 17 subcommands, SARIF + PR annotations, pre-commit hooks, a GitHub Action |
| **AI-agent user** — Cursor / Claude Code / Aider / Zed / Continue | `pip install "django-orm-lens[mcp]"` | 13 read-only MCP tools answering schema questions from ground truth |
MCP setup is one JSON block — see [Integrations](#-integrations). Point `DJANGO_ORM_LENS_ROOT` at your Django project's absolute path.
---
## 🆓 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`](https://github.com/FROWNINGdev/django-orm-lens/blob/main/docs/rules/blast-radius.md) + the [Action](#️-gate-your-ci) |
| Analysis that follows a queryset across functions | [`nplusone`](docs/rules/nplusone.md) |
| Schema drift detection | [`drift`](docs/rules/drift.md) |
| 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`](https://github.com/FROWNINGdev/django-orm-lens/blob/main/docs/rules/blast-radius.md) |
| Cross-layer impact of removing a field | `impact` |
**There is no Pro tier, and none is planned.** If the tool saves you an afternoon, a star is the entire ask.
---
## 📊 Traction
[](https://github.com/FROWNINGdev/django-orm-lens/stargazers)
[](https://github.com/FROWNINGdev/django-orm-lens/network/members)
[](https://github.com/FROWNINGdev/django-orm-lens/graphs/contributors)
[](https://pypi.org/project/django-orm-lens/)
[](https://pepy.tech/project/django-orm-lens)
[](https://open-vsx.org/extension/frowningdev/django-orm-lens)
[](https://marketplace.visualstudio.com/items?itemName=frowningdev.django-orm-lens)
[](https://marketplace.visualstudio.com/items?itemName=frowningdev.django-orm-lens&ssr=false#review-details)
[](https://github.com/FROWNINGdev/django-orm-lens/commits/main)
[](https://registry.modelcontextprotocol.io/)
[](https://smithery.ai/server/@frowningdev/django-orm-lens)
[](https://glama.ai/mcp/servers/FROWNINGdev/django-orm-lens)
[](https://github.com/punkpeye/awesome-mcp-servers)
[](https://mcp.so/servers/django-orm-lens)
> If the tool saves you a `grep` next time you touch a strange Django project — **[a star helps others find it](https://github.com/FROWNINGdev/django-orm-lens/stargazers)**.
### 📈 Star growth
|
### 📚 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.
|
|
### 💥 Blast radius
The review-time question a schema change actually raises: **what does this hit?** Every destructive migration operation becomes a target carrying its risks, every place in the codebase that still reads it, and — for whole-model operations — the cascade fallout.
`migration-risk`, `impact` and `cascade` each answer a third of that; nobody joins them by hand, so the tool does. `--format markdown` is a postable PR comment; `--stats` turns "probably populated" into `~41 000 000 rows, 12.0 GB` from a read-only query you run yourself, with no database credential anywhere near CI.
|
### 🧭 Schema drift
`makemigrations --check` without booting Django. Each app's migrations are replayed in order into the field set they imply, then compared against what `models.py` declares.
Django's own check needs a working settings module, an importable app registry and every dependency installed — unavailable on a cold clone or a broken venv, which is exactly when the answer is cheapest to act on. Only the dangerous direction fails the build: a field declared but never migrated means the column will not exist, and the first query touching it errors.
|
|
### 🎯 Inline diagnostics & QuickFixes (18 rules)
Static analysis over `.py` files with Ruff-style codes (`DOL001`..`DOL041`), Clippy-style `Applicability`, and per-rule severity overrides. `.count() > 0` → `.exists()`, `null=True` on `CharField`, missing `on_delete`, `datetime.now()` → `timezone.now()`, planner-GUC overrides in raw SQL (`enable_*`, `plan_cache_mode`, `jit*`; diagnostic-only), 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).
|