Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

English Β· Русский Β· EspaΓ±ol Β· δΈ­ζ–‡

Django ORM Lens β€” live sidebar and ER diagram for your Django models

Django ORM Lens

The schema intelligence layer for Django.

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.


PyPI Python Django versions CI Downloads License


Install on VS Code Install on Open VSX Docker GHCR


⚑ 10 seconds to first insight

uvx django-orm-lens scan      # or: pipx run django-orm-lens scan

Cold clone, broken venv, no settings module β€” you still get every app, model, field, and relation of the project in your terminal.

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 Sidebar tree, live ER diagram, hover cards, 16 QuickFix rules
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. 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 + the 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. If the tool saves you an afternoon, a star is the entire ask.


πŸ“Š Traction

GitHub stars Forks PyPI monthly Total downloads Marketplace rating Contributors Last commit


MCP Registry Smithery Glama awesome-mcp-servers mcp.so

If the tool saves you a grep next 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 Order model, and how is it connected to User?"

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.


πŸš€ Power features

πŸ’₯ 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 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

Django ORM Lens sidebar showing an app's models with fields, relations, and Meta options

Live sample β€” real django-orm-lens er output, rendered by GitHub right here:

erDiagram
  User {
    CharField display_name
  }
  Tag {
    CharField name
  }
  Post {
    CharField title
    DateTimeField created_at
  }
  Comment {
    TextField body
  }
  Post }o--|| User : "author [CASCADE, as posts]"
  Post }o--o{ Tag : "tags [as posts]"
  Comment }o--|| Post : "post [CASCADE, as comments]"
  Comment }o--|| User : "author [SET_NULL]"
Loading

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') or ManyToManyField(...), with a one-click jump link
  • 🧭 CodeLens β€” above every class Model line: field count, relation count, and an Open ER diagram action
  • 🎨 Named themes β€” auto / default / dark / forest / neutral for 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 list | fzf                   # flat app.Model β€” pipes anywhere
django-orm-lens er > schema.mmd              # ER diagram β€” Mermaid (default)
django-orm-lens er -f dbml > schema.dbml     # …or DBML: paste into dbdiagram.io
django-orm-lens er -f d2 > schema.d2         # …or D2 / plantuml / dot
django-orm-lens diff before.json after.json  # what a PR changes structurally
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          # what one delete() takes down
django-orm-lens impact author                # what still references a field
django-orm-lens blast-radius -f markdown     # risks + who still reads them
django-orm-lens drift                        # migrations vs models, no boot
django-orm-lens stats-sql                    # read-only SQL for --stats

impact, blast-radius, drift and stats-sql ship in py-1.7.0 and later.

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 ten 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
cascade_preview Blast radius of one delete(), grouped by on_delete
er_diagram ER diagram β€” mermaid / dbml / d2 / plantuml / dot
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 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).


πŸ›‘οΈ Gate your CI

Schema regressions are cheapest to catch the moment they enter a PR. Four zero-config ways to block them:

Blast-radius PR bot β€” the whole schema review as one comment, updated in place on every push instead of a new comment each time:

name: Schema review
on: pull_request

permissions:
  contents: read
  pull-requests: write        # only for `comment: true`

jobs:
  blast-radius:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: FROWNINGdev/django-orm-lens@action-v1
        with:
          command: blast-radius
          only-changed: true          # scope to migrations this PR touches
          comment: true               # post once, then update in place
          github-token: ${{ github.token }}

The comment goes up before the job fails, so a blocked PR still explains why. only-changed reads the PR's file list from the API rather than git diff, because actions/checkout defaults to fetch-depth: 1 and the base commit is not in the local history. On push events both flags skip with a notice instead of failing, so one workflow covers both triggers.

The Action installs from PyPI, so blast-radius and drift need py-1.7.0 or later β€” pin it with version: 1.7.0 if your workflow must not drift. To run an unreleased build instead, add install: false and install the source yourself; this repo's own workflow does exactly that, and is what verifies the Action on every PR.

pre-commit β€” two hooks, nothing to install locally:

# .pre-commit-config.yaml
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 Action β€” findings appear as PR annotations with zero extra permissions:

- uses: FROWNINGdev/django-orm-lens@action-v1
  with:
    command: migration-risk      # or: nplusone
    format: github               # ::error / ::warning annotations on the diff

SARIF β†’ Code Scanning β€” findings land in the repo Security tab:

- run: |
    pip install django-orm-lens
    django-orm-lens migration-risk --format sarif --exit-zero > lens.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: lens.sarif

Exit codes are CI-native: diff and nplusone exit 1 on findings, migration-risk and blast-radius exit 1 on critical findings, drift exits 1 when a field is declared but never migrated. Add --exit-zero for report-only mode.


πŸ”Œ 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 βœ…
pre-commit repo: https://github.com/FROWNINGdev/django-orm-lens + two hook ids βœ…
GitHub Actions uses: FROWNINGdev/django-orm-lens@action-v1 β€” annotations or SARIF βœ…
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" }
    }
  }
}

⚑ Performance

The regression suite parses the vendored model graphs of Zulip, Saleor, Wagtail, django CMS, and Mezzanine β€” 59 models across 13,478 lines of real-world models.py β€” in about 20 ms end-to-end on a laptop (21 ms best-of-3 on the repo's golden-fixture corpus; a <2 s guard runs in CI on every matrix cell).

Reproduce it yourself:

git clone https://github.com/FROWNINGdev/django-orm-lens && cd django-orm-lens/cli
pip install -e . && python -m pytest tests/test_golden_fixtures.py tests/test_golden_snapshots.py -q

🎯 Who this is for

  • Django developers joining a codebase with 10+ apps and getting lost in models.py sprawl.
  • 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, no manage.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-graph has not been updated since 2023-05 and does not test Django 5.x.

When you want something else

Honest boundaries: profiling a live request β†’ django-debug-toolbar. Historical request profiling β†’ django-silk. Query-count assertions inside a test suite β†’ django-perf-rec. Production APM on real traffic β†’ Scout / Sentry. Django ORM Lens deliberately stays static β€” it's the layer that works before the app can even boot, and the only one your CI and your AI agent can use on any checkout.


βš™οΈ 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

Sixteen editor-side checks (DOL001–DOL032) with Ruff-style codes, per-rule severity, and Clippy-style applicability β€” plus fifteen CLI-side migration-risk rules and the static N+1 analyzer. Every rule now has its own documentation page.

Category Rules Examples
Queryset DOL001–DOL007 .count() > 0 β†’ .exists(), FK access in loops (N+1)
Model definition DOL011–DOL015 ForeignKey without on_delete, null=True on string fields
Datetime DOL021–DOL022 datetime.now() β†’ timezone.now()
Forms / views DOL031–DOL032 locals() in render(), Meta.fields = '__all__'
Migration risks 16 rules NOT NULL add without default, table-locking index builds, irreversible data migrations
Static N+1 1 analyzer FK/M2M access in loops without select_related / prefetch_related

β†’ Full rule reference β€” every code with bad/good examples, QuickFix behaviour, and suppression syntax.

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_model on 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 without select_related/prefetch_related)
  • v0.6.0 β€” CLI migration-risk β€” flags risky operations in migrations/*.py (15 rules today)
  • 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_MODEL resolves 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 β€” --verbose no longer walks the tree twice; WorkspaceIndex.scanned_files carries 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](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_boy scaffold 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_name honoured)
  • v0.8.0 β€” Sidebar UX overhaul: stable TreeItem.id, MarkdownString tooltips with command: deep-links, FileDecorationProvider badges, TreeView.badge on the activity bar, three when-gated viewsWelcome states

v1.5.0 β€” the "one core, three surfaces" wave

  • CI formats: SARIF 2.1.0 + --format github PR annotations for nplusone and migration-risk
  • Four analyzers promoted from MCP-only to the CLI: suggest-indexes, signals, migration-deps, cascade
  • er --format dbml | d2 | plantuml | dot β€” community-standard diagram exports (dbdiagram.io, D2, PlantUML, Graphviz β€” dot contributed by @JJordan0C)
  • Three new migration-risk rules: runpython_no_reverse, alter_unique_together_lock, alter_index_together_deprecated β€” 15 total
  • pre-commit hooks (django-orm-lens-nplusone, django-orm-lens-migration-risk) + composite GitHub Action
  • docs/rules/ β€” a documentation page for every rule (19 pages)
  • Golden-snapshot regression suite over 59 real-world models (Zulip / Saleor / Wagtail / django CMS / Mezzanine); ruff + mypy now gate CI
  • Migration dependency graph β€” migration-deps (text / json / mermaid)

py-1.7 β†’ 1.8 β€” the schema-intelligence wave

  • blast-radius β€” migration risks joined with what still reads the schema they touch, as a PR bot (comment: true, sticky, only-changed)
  • drift β€” makemigrations --check without booting Django
  • impact <name> β€” what still references a model or field, grouped by Django layer
  • blast-radius --stats + stats-sql β€” optional production row counts from read-only SQL you run yourself (the tool never holds a DB credential)
  • nplusone resolves across functions β€” a queryset returned by a helper is followed into the loop that consumes it
  • blast_radius, drift and impact exposed as MCP tools β€” thirteen tools for AI agents
  • drift documents its !! / ~ marks in the report and in --help β€” reported by @sevdog (#57)
  • drift follows inheritance from abstract bases β€” an abstract base's fields count as the concrete child's own, as Django treats them β€” reported by @sevdog (#58)
  • suggest-index recognises the indexes Django already made β€” primary key (pk and id are one lookup), db_index, unique, foreign keys, unique_together, UniqueConstraint β€” reported by @sevdog (#60), same cause independently found by @RinZ27 (#61)

Next

  • ORM query autocomplete inside .filter() / .exclude() / .annotate() (#3)
  • App / model toggle checkboxes to declutter huge schemas
  • DOL rule engine ported into the Python CLI β€” one rule catalogue, three surfaces

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

Do you send any of my code to a server?
No. Every byte stays on your machine. The parser is pure TypeScript (extension) or pure Python (CLI). No LLM calls, no telemetry, no analytics, no error reporting. The Mermaid renderer runs inside VS Code's webview sandbox.
Does it work with Poetry / uv / conda / no venv at all?
Yes. The extension reads Python source directly β€” it does not import Django and does not care what package manager you use. The CLI requires Python 3.9+, but that is it.
My models are split across multiple files inside a models/ package. Does that work?
Yes, since v0.2.0. Both the extension and the CLI walk models/*.py alongside classic models.py.
Can I use it with DRF serializers, Wagtail, Oscar, or third-party base models?
Any class that looks like a Django model is picked up: subclasses of 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.
Which AI agents can use the MCP server?
Any MCP-compatible client β€” Cursor, Aider, Continue.dev, Zed, and any other tool that speaks the protocol. Just point command at the installed django-orm-lens-mcp binary. See the Integrations section.
How do I block schema regressions in CI?
Three ways, all zero-config: the two pre-commit hooks, the composite GitHub Action (uses: FROWNINGdev/django-orm-lens@action-v1 with format: github for PR annotations), or --format sarif piped into github/codeql-action/upload-sarif for the Security tab. diff / nplusone exit 1 on findings, migration-risk exits 1 on critical findings.
Is there a JetBrains / PyCharm version?
Not yet. PyCharm's Django Structure tool window is already good, so the value delta is smaller. If enough people ask, it becomes worth doing.

πŸ†˜ Support


πŸ“œ License

MIT Β© FROWNINGdev


Made for developers who care about their codebase.

Marketplace Β· PyPI Β· GitHub Β· Issues Β· Discussions Β· Sponsor

About

Free, MIT alternative to paid Django schema review. Blast radius on every PR, schema drift, N+1 across functions, ER diagrams, MCP server. No DB, no Django boot, no Pro tier.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages