Skip to main content
Glama
FROWNINGdev

django-orm-lens

by FROWNINGdev

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.

PyPI Python Django versions CI Downloads License

Install on VS Code Install on Open VSX Docker GHCR

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 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, 17 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.


Related MCP server: django-mcp-inspector

🆓 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 Contributors PyPI monthly Total downloads Open VSX downloads VS Code installs Marketplace rating 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 (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 themesauto / 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.1
    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 (DOL001DOL032) 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

DOL001DOL007

.count() > 0.exists(), FK access in loops (N+1)

Model definition

DOL011DOL015

ForeignKey without on_delete, null=True on string fields

Datetime

DOL021DOL022

datetime.now()timezone.now()

Forms / views

DOL031DOL032

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.1through_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.0settings.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)

  • driftmakemigrations --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)

  • A sixth golden fixture — Read the Docs joins Zulip, Saleor, Wagtail, django-CMS and Mezzanine, putting the parser under 75 models and 538 fields of real-world Django — contributed by @JJordan0C (#62, closing #51)

  • django-taggit TaggableManager is read as the M2M it is — through taggit.TaggedItem to taggit.Tag, through= overrides honoured, in both the Python and the TypeScript parser — contributed by @Guflly (#63, closing #50)

  • DOL021 states the USE_TZ default correctly — False through Django 4.2, True from 5.0, with the startproject template's USE_TZ = True since 4.0 called out as the separate thing it is — and no longer claims timezone.now() is always aware UTC — found by @Justine0211 while translating the page (#52)

  • The parity_input.py fixture carries the models import a real models.py would have — contributed by @RinZ27 (#64)

py-1.9 → 1.12 — the real-checkout wave

Found by running the CLI over actual checkouts of django-oscar, django-guardian, django-allauth and django-cms rather than over fixtures. Every one of these was invisible to a green test suite, and two of them made the tool answer confidently with something false.

  • Models declared inside a module-level block parse — the swappable-model idiom (if not is_model_registered(...): and then an indented class) that every pluggable Django framework uses, against class discovery anchored on ^class. django-oscar went from 12 models, every one of them from its own tests/ directory, to 82. All six golden snapshots stayed byte-identical: a column-0 class parses exactly as before

  • abstract_models.py is read alongside models.py — pluggable frameworks keep the abstract base there and leave models.py holding only the concrete subclass, so 72 of django-oscar's 83 models reported zero fields between them. Now 8, and those 8 are correct: they subclass concrete models, where multi-table inheritance leaves the columns on the parent's table

  • drift no longer fails a build over two app directories sharing a name — replayed migration state is merged per app name, matching how the declared side is already keyed. On a real django-guardian checkout the blocking count goes 1 → 0 and the contradictory duplicate row disappears, while a genuinely unmigrated field still blocks

  • django-mptt models are no longer invisible — MPTTModel is a recognised base, and TreeForeignKey / TreeOneToOneField / TreeManyToManyField are reported as the Django fields they subclass, so TreeForeignKey('self', ...) draws exactly the self-edge a plain ForeignKey('self', ...) does. No django-mptt dependency is added — the parser keeps working against a broken venv. Saleor's product.Category and its children edge now appear in the golden snapshot: 76 added lines, none removed (closing #49)

  • The MCP server reports its own version — FastMCP forwards none, so the SDK fell back to importlib.metadata.version("mcp") and every initialize response named the wrong project's release number to the client

v0.9 → v0.12.1 — the extension catches up

  • v0.9.0 — Partial UniqueConstraint tracking in Time-Travel Schema Diff: add / drop / change / rename as typed events, with fromCondition carrying the pre-change predicate so a review comment can show Q(is_primary=True) → Q(is_primary=True, deleted=False), and a rename no longer showing up as a lossy add + drop pair. Multiple unnamed constraints on one model key onto #anon-<index> instead of collapsing into a single event. Prompted by django-extensions #1813, where sqldiff drops the condition= predicate so migration reviewers never see what changed

  • v0.10.0 — Impact-analysis layer detection runs on the workspace-relative path. It used to match /tests/ and /views.py anywhere in a file's absolute path, so a project checked out under any directory called tests — or a monorepo with services/tests/ above it — had every file reported as that layer, views.py as a test, admin.py as a test. Also: webview messages validated by origin rather than by source, and conflicting leaf migrations detected — two migrations claiming the same parent, which Django only complains about at migrate time

  • v0.10.1 — The Marketplace listing names impact analysis, blast radius and schema drift, and says plainly that the tool is free and MIT with no Pro tier. The store page had still described the extension as a sidebar and an ER diagram — what it was two waves earlier — so nobody searching for those features found it. Metadata takes effect only on publish, which is why it needed a release of its own

  • v0.11.0 — The TypeScript half of django-mptt support, cut as its own release rather than folded into a later one: between py-1.12.0 shipping and this build, the CLI and the extension disagreed about what a django-mptt schema contains, which is the exact failure the shared golden fixture exists to prevent

  • v0.12.0 — The extension asks for a GitHub star, on the third user-initiated ER-diagram open. Not on install: a prompt arriving before the tool has done anything gets dismissed reflexively, and that dismissal is permanent in the user's mind. Sidebar refreshes that re-render an already-open panel are not counted — they are not the user asking for anything. "Later" and "Don't ask again" are stored as separate states, so a deferral re-arms the ask exactly once, twelve opens later; two prompts is the lifetime maximum. The policy is a pure function covered by six tests that need no VS Code host

  • v0.12.1 — Export as SVG wrote an unopenable file. toSvg returns percent-encoded markup where toPng returns base64; the save path assumed base64 for anything starting with data:, and base64-decoding percent-encoded text does not fail — the decoder silently drops every character outside its alphabet and returns bytes. A 48-character <svg> document reached disk with the right name, a plausible size and no valid content anywhere in it. The transfer encoding is now read from the data-URL header instead of guessed from the prefix

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-model-utils (django-taggit shipped in py-1.11.0, django-mptt in py-1.12.0 / v0.11.0)

  • JetBrains / PyCharm plugin (if there is demand)

Vote by 👍-ing the corresponding issue.

❓ FAQ

🆘 Support

📜 License

MIT © FROWNINGdev

Made for developers who care about their codebase.

Marketplace · PyPI · GitHub · Issues · Discussions · Sponsor

Available Tools

5 tools
describe_modelA

Full JSON detail for one model: fields, relations, Meta, base classes, file path.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It lists the content included (fields, relations, etc.) but does not disclose behavioral traits such as read-only nature, authentication needs, or rate limits. The presence of an output schema reduces the need to describe return structure, but safety profile and side effects are unaddressed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that front-loads the key purpose ('Full JSON detail for one model') and lists all relevant components. Every word contributes value with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description names the types of information returned (fields, relations, Meta, base classes, file path), which is useful given the output schema exists. However, it fails to explain how to specify the model parameter (format) and does not address any prerequisites or constraints, leaving gaps for a simple tool with one parameter.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'model' (string) has 0% schema description coverage. The tool description does not clarify the expected format (e.g., app_label.ModelName) beyond stating it identifies a model. The description fails to add meaning for the parameter, which is a significant gap given the lack of schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns 'Full JSON detail for one model' and lists specific components (fields, relations, Meta, base classes, file path). It distinguishes from siblings like 'er_diagram' and 'list_models' by focusing on a single model's detailed content.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for retrieving full details of a single model but does not explicitly state when to use this versus siblings (e.g., use find_relations for relations only, list_models for overview). No exclusions or context signals are provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

er_diagramB

Emit a Mermaid erDiagram for the whole workspace.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must fully disclose behavior. It hints at output (Mermaid diagram) but does not specify if it is read-only, permissions needed, or any side effects. Lacks depth.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, very concise. For a simple zero-parameter tool, this is acceptable, though it could include more detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters and existence of output schema, the description is minimal but adequate. However, it doesn't clarify whether the diagram covers all models or any filtering, nor the exact output format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist, so schema coverage is 100%. The description need not add parameter info, and baseline for 0 params is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool emits a Mermaid erDiagram for the whole workspace, using a specific verb and resource. This distinguishes it from siblings like describe_model (single model) or find_relations (specific relations).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives such as describe_model or find_relations. The description only states what it does, not the context or trade-offs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_relationsC

Outbound (this model → others) and inbound (others → this) relations.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, and the description does not disclose behavioral traits (e.g., read-only, permissions, output format). It only states the basic purpose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short and front-loaded, but it sacrifices valuable information that could clarify parameter usage and behavior. More detail would improve it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low schema coverage and no annotations, the description is too brief to be complete. It does not explain the output or how to use the model parameter properly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'model' has no description in the schema (0% coverage), and the description does not clarify what 'model' refers to (name, ID, etc.). No added meaning beyond the parameter name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns both outbound and inbound relations for a model, distinguishing it from sibling tools like describe_model or list_models.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives. Sibling tools are listed but without any comparison or context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_appsA

List every Django app in the workspace with model counts.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, description carries full burden. It indicates read-only behavior ('list') and completeness ('every'), but doesn't disclose performance implications or permission requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence with zero wasted words. Front-loaded with verb and resource, immediately conveying purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameter-less tool with output schema, description sufficiently covers what the tool does. Could optionally mention output format but schema handles it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist, so schema coverage is 100%. Description adds value by specifying output includes model counts, which is beyond the schema's emptiness. Baseline for 0 params is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states action ('list'), resource ('every Django app'), and distinctive output ('with model counts'). Differentiates from sibling tools like list_models which focus on models within apps.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use or alternatives. However, the tool's simplicity (no parameters) implies straightforward usage. Lacks suggestions for when not to use or comparison with list_models.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_modelsC

Flat list of app.Model. Optional 'app' filter.

ParametersJSON Schema
NameRequiredDescriptionDefault
appNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must fully convey behavior. It mentions 'Flat list' and an optional filter but provides no details on default behavior (e.g., when app is empty), pagination, ordering, or performance implications.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no fluff. It efficiently conveys the core purpose and filter option.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has one optional parameter and an output schema, the description covers the basic idea but lacks context about what an 'app.Model' is, the structure of the list, or any constraints. The output schema exists, partially compensating, but for a listing tool more context is desirable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds that 'app' is an optional filter, which is helpful, but does not explain expected format or behavior when omitted. Overall minimal added value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Flat list of app.Model' clearly indicates the tool returns a list of models, distinguishing it from siblings like 'describe_model' (single model) and 'list_apps' (apps). However, it lacks an explicit verb like 'Retrieve' or 'List'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool vs alternatives like 'describe_model' or 'find_relations'. The description only implies basic usage with an optional filter.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 5 tool updatesv0.1.0
    • First observeddescribe_model
    • First observeder_diagram
    • First observedfind_relations
    • First observedlist_apps
    • First observedlist_models

TDQS

A3.6/5.0
Disambiguation5/5

Each tool serves a distinct purpose: listing apps/models, describing a single model, finding relations, and generating an ER diagram. No overlap in functionality.

Naming Consistency4/5

Most tools follow a clear verb_noun pattern (list_apps, list_models, describe_model, find_relations). The exception is 'er_diagram' which is noun_noun, though still readable.

Tool Count5/5

With 5 tools, the set is well-scoped for Django ORM introspection. Each tool earns its place, covering essential operations without redundancy.

Completeness5/5

The tool surface covers the full lifecycle of ORM exploration: listing apps/models, retrieving model details, analyzing relations, and visualizing the schema. No obvious gaps.

Maintenance

ActivityActive
ResponsivenessResponsive

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    An MCP server for inspecting and interacting with Pydantic models and Python type contracts. It enables LLMs to perform deterministic validation, schema generation, model explanation, and Pydantic v1 to v2 migration analysis.
    11
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Exposes Django runtime information (settings, apps, URLs, models, migrations) as MCP resources for AI agents to inspect a live Django project without static analysis.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    A Model Context Protocol (MCP) server for developing Django applications. It exposes Django project information through MCP tools, enabling AI assistants to better understand and interact with Django codebases.
    111
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FROWNINGdev/django-orm-lens'

If you have feedback or need assistance with the MCP directory API, please join our Discord server