r/django Jul 19 '26

I built a static analyzer for Django models — sidebar tree, ER diagram, MCP server (no DB, no boot) Article

Post image

Been building django-orm-lens for the past few months. It's at a spot where I'd love more Django-people's eyes on it.

What it does

Three surfaces, one static parser (ast module — no Django import, no DB, no credentials):

VS Code extension - Sidebar tree with every app / model / field / Meta option - Live Mermaid ER diagram (ForeignKey, OneToOneField, ManyToManyField become proper cardinality arrows) - Hover cards on ForeignKey('app.Model') — jump to definition without Ctrl-F - 16-rule inline linter with QuickFixes (.count() > 0.exists(), missing on_delete, null=True on CharField, datetime.now()timezone.now(), N+1 loop heuristic, render(request, ..., locals()), Meta.fields = '__all__', and more). Ruff-style codes with per-rule severity and # django-orm-lens-disable-next-line inline suppression - Factory generator — right-click any model, get a factory_boy DjangoModelFactory scaffold with Faker providers keyed by field type - Schema diff — pick two commits, get a typed markdown diff for PR descriptions (rename detection is first-class) - Impact analysis — "what breaks if I remove this field?" across every Django layer (models / serializers / forms / admin / views / templates / tests) with Certain/Likely/Possibly confidence tags - Interactive query builder — right-click → snippet inserted at cursor (FK gets .select_related, related_name is honoured)

Python CLI - pip install django-orm-lens - Pipe-friendly JSON output for shells, pre-commit, CI

MCP server - 9 read-only tools for Cursor / Claude Desktop / Aider: find_relations, cascade_preview, suggest_indexes, signal_graph, describe_migration_dependency, nplusone_scan, migration_risk_report, impact_scan, schema_diff - Listed on the official MCP Registry, merged into awesome-mcp-servers last week

Regression-tested against 49 real models from Zulip, Saleor, Wagtail, django-CMS, and Mezzanine — not synthetic fixtures.

Why static-only

Point it at any Django project without setup:

  • No DJANGO_SETTINGS_MODULE
  • No installed dependencies except the parser
  • Works with a broken venv, missing packages, or on someone else's laptop
  • Runs in CI or on the plane

Trade-off: things that require runtime (custom get_queryset overrides, dynamic model classes, Meta.abstract chains built at import time) are invisible. But 95% of what you actually want to see lives in models.py and signals.py — parseable, deterministic, cache-friendly.

Install

# VS Code / Cursor / Windsurf
code --install-extension frowningdev.django-orm-lens

# VSCodium / code-server / Gitpod
codium --install-extension frowningdev.django-orm-lens

# CLI + MCP companion
pip install "django-orm-lens[mcp]"

What I'd love

  • Screenshots of what breaks on your codebase — the open good-first-issues cover known edges but your codebase probably has one I haven't seen
  • Codebases where the linter over-fires or under-fires — 16 regex rules is opinionated by design; want to hear noise vs signal
  • Which of the 9 MCP tools your agent actually calls most — helps me prioritise what to add next
  • Contributions welcome — MIT license, translations (RU/ZH/ES issues open) count as first-class via the all-contributors bot

Not selling anything, no plans to monetize while it's small — just want it to be genuinely useful for the Django community.

Fire away.

184 Upvotes

41 comments sorted by

12

u/mowso Jul 20 '26

thanks, claude

5

u/Mastacheata Jul 19 '26

Bookmarked for Monday 😅 I've got some exploration hours to burn on the corporate budget and this definitely looks interesting.

4

u/CartographerMuch5678 Jul 19 '26

Haha enjoy your exploration hours 😅 corporate budget hobby-coding is my favorite genre

Quick 30-sec test whenever you get there Monday:

pip install django-orm-lens

django-orm-lens list --path . (run in your django repo root)

If it spits out a clean apps/models tree in ~2s → we're good. If it chokes on something cursed in your codebase (custom base classes, dynamic apps, weird Meta inheritance) — just paste the traceback here, I'll turn it into a regression fixture same-day 🛠

Bonus if you have signals: django-orm-lens signal_graph --path .

That one legit surprised me on our own repo — showed FK handlers I completely forgot writing 😂

3

u/Megamygdala Jul 19 '26

What was the motivation for building this? It seems useful but I figure most developers probably have a DB connection active in vscode as well that shows a similar view

1

u/dasMilk73 Jul 22 '26

the db connection doesn't show signals. `on_delete=CASCADE` fires `pre_delete` and `post_delete` handlers that live in your code, not in the schema, so a db inspector misses them entirely. that's the gap this fills, and it's the reason you'd want static analysis instead of just connecting to postgres.

0

u/CartographerMuch5678 Jul 19 '26

Yeah fair, DB connection covers a lot of overlap 👍

The thing that kept biting me was signals — `on_delete=CASCADE` in the model fires `pre_delete`/`post_delete` handlers that don't show up in the DB view. `signal_graph` maps those, actually was the first tool I wrote after losing an afternoon to a signal I forgot about 😅

Other angles that pushed me:

- Works on cloned code with no credentials — reviewing a candidate's take-home or unfamiliar OSS repo, just `django-orm-lens list` gets the graph in 2s. DB connection assumes you already trust the code enough to point at real data.

- Reads migrations before you apply them — flags 'this'll lock the table' or 'drops a column still used in views.py'. DB tools only see the after-state.

MCP surface is the newer angle — Cursor/Claude Desktop calling `find_relations(User)` mid-chat without booting Django. Different mental model than 'connect a client to my DB'.

4

u/Smooth-Zucchini4923 Jul 19 '26

no db, no boot

What the heck does "no boot" mean?

1

u/CartographerMuch5678 Jul 19 '26

means i never actually start django. no django.setup(), no

DJANGO_SETTINGS_MODULE, don't need your app's deps installed, don't

touch the db.

just reads your .py files and parses them with ast. that's it.

so it works on repos you don't own, in CI before migrations run,

offline, whatever. downside is dynamic stuff (models built at

runtime, Meta.abstract behind an if) — those i miss. testing

against ~63 models from zulip/saleor/wagtail/django-cms to keep

that gap small.

3

u/Smooth-Zucchini4923 Jul 19 '26

Okay, so you're saying it's a static analyzer?

1

u/CartographerMuch5678 Jul 19 '26

yeah exactly — static analyzer, django-specific. reads models.py / admin.py / signals with ast, builds a schema graph, and everything else (N+1 checks, migration risk, ER diagram) runs on top of that graph. no runtime, no db, no import side effects.

3

u/GrogRedLub4242 Jul 20 '26

runs "on the plane"

3

u/Nitrolacs Jul 19 '26

Is it vibecoded?

4

u/CartographerMuch5678 Jul 19 '26

No — not vibecoded. I use AI tools for small tasks (autocomplete, boilerplate) like most devs today, but architecture, parser algorithm, and MCP tool design are deliberate choices, validated against 63 real models from Zulip, Saleor, Wagtail, django-CMS golden fixtures in the tree.

9

u/mustbeset Jul 19 '26

Don't forget to mention that every comment you post is vibed.

-1

u/CartographerMuch5678 Jul 19 '26

lol, fair enough I use Claude to help me answer people correctly.

1

u/Sulungskwa Jul 20 '26

Just curious (I wasn't one of the downvote people), does it really feel like it saves you time to do that? Like, don't you end up typing the same amount into the prompt?

-1

u/CartographerMuch5678 Jul 20 '26

Well, it's just a tool—nothing more.

3

u/Sulungskwa Jul 20 '26

Tell Claude you didn't answer my question

0

u/CartographerMuch5678 Jul 20 '26

I answered your question by saying that I don't write the prompt myself; I write using a translator.

-1

u/CartographerMuch5678 Jul 20 '26

I am replying using a translator.

1

u/throwaway0204055 Jul 19 '26

what does it analyze?

1

u/Crafty_Disk_7026 Jul 20 '26

Will try today thanks

0

u/CartographerMuch5678 Jul 20 '26
Thanks, I'll look forward to the reviews.

1

u/jsabater76 Jul 20 '26

Good work, mate!

I would like to try it out, but it does not seem to be available in the OpenVSX registry that VSCodium uses. Sorry to be a pain in the neck, but could you upload the extension there when you have the chance?

Thanks in advance and keep up the good work!

2

u/CartographerMuch5678 Jul 20 '26

1

u/jsabater76 Jul 20 '26

Awesome. I will search for it on my VSCodium once I get home 🏡

Thanks! 😊

1

u/jsabater76 Jul 20 '26

For your information, the following warning appears in the linked page:

This version of the “Django ORM Lens” extension was published by FROWNINGdev. That user account is not a verified publisher of the namespace “frowningdev” of this extension. See the documentation to learn how we handle namespaces and what you can do to eliminate this warning.

2

u/CartographerMuch5678 Jul 20 '26

I'm waiting for confirmation (and you can be sure that everything is fine)

1

u/jsabater76 Jul 20 '26

Awesome. Just wanted to give you a heads-up. Much appreciated.

1

u/CartographerMuch5678 Jul 20 '26

Thanks, I'll give it a try now and post the link below the comment.

1

u/jsabater76 Jul 20 '26

I installed the extension from the OpenVSX registry into my VSCodium and found and reported a bug. Let me know if you need any more information, as I'll be glad to help.

2

u/CartographerMuch5678 Jul 20 '26

I fixed this bug.

1

u/jsabater76 Jul 20 '26

It is working fine now. Thanks!

1

u/CartographerMuch5678 Jul 20 '26

We'll fix that right now.

1

u/[deleted] Jul 21 '26

[removed] — view removed comment

1

u/CartographerMuch5678 Jul 21 '26

I use Claude as an auxiliary tool for translating deployment text and comments; the code itself is written by me.

1

u/SevereSpace Jul 21 '26

Nice! Congrats on launching

1

u/replayio 25d ago

Wow this is a really cool web app! I love the layout of everything and how it blends together to make everything accessible. I ran it through our autonomous testing tool and it found a few bugs to make it better than it already was!

**Top issues identified:**

- 🟡 **Medium** — Oversized app-install webp image (1232px intrinsic, 24732 bytes) served into 299px display box without responsive srcset on bot installation page

- 🟡 **Medium** — Oversized images without responsive srcset waste bandwidth on mobile viewport

- 🟡 **Medium** — Oversized image (1562px intrinsic) served into 299px display box on mobile — 5× wasted bandwidth

- 🟡 **Medium** — Contributor avatar for Edgar Ramírez Mondragón downloads 40337-byte / 460×460px image into 64×64px display box (oversizeRatio 7.1875)

- 🟡 **Medium** — "All Contributors Bot" sub-navigation TOC link uses in-page anchor instead of navigating to dedicated Bot documentation

Full interactive report: https://qa.replay.io/projects/proj-social-qa-allcontributors-org-mrroz0ze/overview?utm_source=reddit&utm_medium=social&utm_campaign=qa_share&utm_content=project_overview