django-orm-lens

MCP serverAI & models

django-orm-lens gives your AI a clear view of a Django project's database layer. Once added, your AI can examine models, relations, migrations, indexes, and signals, and flag N+1 query issues. Use it to understand how your data is structured before making changes.

Unavailable. This server has no hosted endpoint yet, so Ahel can't serve it.

Add to setup to save this item as a reference. Ahel cannot run it, and signing in will not install it.

What your AI can do with it

  • Inspect Django models and their fields
  • Map how models relate to each other
  • Review migrations
  • Check which indexes exist
  • Find signal handlers
  • Spot N+1 query issues

Getting started

  1. Save this item in Your setup as a reference.
  2. Read the source or reference documentation for its setup requirements. Saving it here does not connect it to your AI.
  3. Check this page for availability before trying to install it through Ahel.

From the project's README

As published by frowningdev/django-orm-lens in README.md.

English · Русский · Español · 中文

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.

Featured in Django News #347 · PyCoder's Weekly #746


⚡ 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 areInstallYou get
Editor user — VS Code / Cursor / Windsurf / VSCodiumcode --install-extension frowningdev.django-orm-lensField autocomplete in .filter(), sidebar tree, live ER diagram, hover cards, 17 QuickFix rules
Terminal / CI userpip install django-orm-lens17 subcommands, SARIF + PR annotations, pre-commit hooks, a GitHub Action
AI-agent user — Cursor / Claude Code / Aider / Zed / Continuepip 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 tierHere
PR review bot for schema changes — posts once, then updates in placeblast-radius + the Action
Analysis that follows a queryset across functionsnplusone
Schema drift detectiondrift
Index proposals from observed QuerySet usagesuggest-indexes
Migration risk weighed against real table sizesblast-radius --stats
Blast radius of a destructive migrationblast-radius
Cross-layer impact of removing a fieldimpact

There is no Pro tier, and none is planned. If the tool saves you an afternoon, a star is the entire ask.


📊 Traction

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 (17 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

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]"

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 thirteen read-only tools:

ToolPurpose
list_appsEvery Django app in the workspace with model counts
list_modelsFlat app.Model list, optional app filter
describe_modelFull field / relation / Meta detail for one model
find_relationsInbound + outbound relations for one model
cascade_previewBlast radius of one delete(), grouped by on_delete
er_diagramER diagram — mermaid / dbml / d2 / plantuml / dot
describe_migration_dependencyPer-app migration DAG: roots, leaves, cross-app deps
suggest_indexesMeta.indexes proposals from observed QuerySet usage
signal_graphSender→signal→handler graph from @receiver decorators
blast_radiusWhat a destructive migration hits: its risks, the code still reading it, the cascade fallout
driftmakemigrations --check without booting Django — migrations diffed against models.py
impactEvery reference to a field or model name, grouped by Django layer
nplusone_scanStatic 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.1
    hooks:
      - id: django-orm-lens-nplusone
      - id: django-orm-lens-migration-risk

GitHub Action — findings appear as PR annotations with zero extra permissions:

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
84
Forks
13
Last commit
Oct 2026
Advanced
Delivery
django-orm-lens MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-frowningdev-django-orm-lens
Source
github.com/frowningdev/django-orm-lens