Skip to content

Support edge labels for one-to-one and through relations 🩷 - #5

Merged
FROWNINGdev merged 2 commits into
FROWNINGdev:mainfrom
kingrubic:fix/edge-relation-labels
Jul 13, 2026
Merged

Support edge labels for one-to-one and through relations 🩷#5
FROWNINGdev merged 2 commits into
FROWNINGdev:mainfrom
kingrubic:fix/edge-relation-labels

Conversation

@kingrubic

@kingrubic kingrubic commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve existing OneToOneField metadata labels in the ER diagram
  • parse ManyToManyField through= arguments and include the intermediate model in edge labels
  • add a regression test covering both relation types

Tests

  • npm test
  • npm run build
  • npm run package -- --out /tmp/django-orm-lens-issue-2.vsix
  • git diff --check

Closes #2

Summary by CodeRabbit

  • New Features
    • Mermaid relationship diagrams now include intermediary (“through”) model details for many-to-many relationships.
    • Relationship edge labels now surface additional metadata such as cascade behavior, related names, and through-model information.
  • Bug Fixes
    • Improved Django relationship parsing so generated diagram connections better match configured model relationships.
  • Tests
    • Added automated coverage to verify the generated edge labels include through-model and related metadata.
    • Added an npm test script to build the extension and run the Node test runner.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/edge-labels.test.js (1)

5-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore Module._load after the test to avoid global side effects.

The vscode mock replaces Module._load globally but never restores it. While node --test typically runs each file in its own process, restoring the original loader is good hygiene and prevents interference if test isolation changes.

♻️ Proposed fix
 const originalLoad = Module._load;
 Module._load = function (request, parent, isMain) {
   if (request === 'vscode') {
     return {};
   }
   return originalLoad.call(this, request, parent, isMain);
 };
+
+// Restore the original loader after all tests in this file complete.
+test.after(() => {
+  Module._load = originalLoad;
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/edge-labels.test.js` around lines 5 - 11, Restore Module._load to
originalLoad after the test completes, ensuring the temporary vscode mock in the
Module._load override does not persist globally. Use the existing originalLoad
symbol and the test’s teardown mechanism rather than changing the mock behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/parser.ts`:
- Around line 112-117: Update the regular expression in extractThroughModel to
require a word boundary immediately before “through”, preventing matches within
longer argument names while preserving the existing value-capture behavior.

---

Nitpick comments:
In `@test/edge-labels.test.js`:
- Around line 5-11: Restore Module._load to originalLoad after the test
completes, ensuring the temporary vscode mock in the Module._load override does
not persist globally. Use the existing originalLoad symbol and the test’s
teardown mechanism rather than changing the mock behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42da5480-f993-45a1-beac-3b80f1c70598

📥 Commits

Reviewing files that changed from the base of the PR and between 31fdfba and a479842.

📒 Files selected for processing (5)
  • package.json
  • src/graphWebview.ts
  • src/parser.ts
  • src/types.ts
  • test/edge-labels.test.js

@FROWNINGdev

Copy link
Copy Markdown
Owner

Thanks for the contribution! The through-model handling with the \b boundary is exactly right (correctly ignores passthrough=), and the regression test covers both branches. Setting to auto-merge once the workflow completes.

@FROWNINGdev
FROWNINGdev merged commit 9ab5576 into FROWNINGdev:main Jul 13, 2026
2 checks passed
FROWNINGdev added a commit that referenced this pull request Jul 13, 2026
Mirrors the throughModel field added to the TypeScript parser in #5 (thanks
@kingrubic). Extracts `through=` from ManyToManyField args and emits the
intermediate model name in the JSON schema under `throughModel`, keeping the
CLI and the VS Code extension in lock-step.

Version bump + publish deferred to the next automated firing.
FROWNINGdev added a commit that referenced this pull request Jul 13, 2026
- Python parser: emit throughModel on M2M fields (parity with TS)
- Bump extension 0.3.0 -> 0.3.1
- Bump CLI 1.0.3 -> 1.0.4
- server.json aligned to 1.0.4 for MCP Registry
- CHANGELOG: credit @kingrubic (PR #5) for throughModel

Co-authored-by: kingrubic <baozidoge2@gmail.com>
FROWNINGdev pushed a commit that referenced this pull request Jul 15, 2026
* fix: label one-to-one and through relations

* test: harden through relation parsing
FROWNINGdev added a commit that referenced this pull request Jul 15, 2026
Mirrors the throughModel field added to the TypeScript parser in #5 (thanks
@kingrubic). Extracts `through=` from ManyToManyField args and emits the
intermediate model name in the JSON schema under `throughModel`, keeping the
CLI and the VS Code extension in lock-step.

Version bump + publish deferred to the next automated firing.
FROWNINGdev added a commit that referenced this pull request Jul 15, 2026
- Python parser: emit throughModel on M2M fields (parity with TS)
- Bump extension 0.3.0 -> 0.3.1
- Bump CLI 1.0.3 -> 1.0.4
- server.json aligned to 1.0.4 for MCP Registry
- CHANGELOG: credit @kingrubic (PR #5) for throughModel

Co-authored-by: kingrubic <baozidoge2@gmail.com>
FROWNINGdev added a commit that referenced this pull request Jul 23, 2026
Pick a models.py, pick two commits, get a typed diff rendered as a
copy-paste-ready markdown fragment for PR descriptions.

Blueprint distilled from proven prior art (research-first):

  Atlas (ariga/atlas) — analyzer/fixer divorce, typed events grouped
    by table, color-coded chips. Copied: structured event model,
    grouping, first-class renames.
  Prisma migrate diff  — renames as their own event, never as
    drop+add. Copied verbatim.
  Community pattern (git log --follow + AST diff on models.py) —
    packaged for the first time.

Structured events, not text

  AddModel / DropModel / RenameModel (confidence-scored)
  ModifyModel
    AddField / DropField / RenameField (confidence-scored)
    ChangeFieldType (AutoField -> BigAutoField)
    ChangeFieldOption (whitelisted: null/blank/unique/default/db_index/
      primary_key/max_length/max_digits/decimal_places/on_delete/
      related_name/auto_now/auto_now_add — verbose_name/help_text are
      excluded as noise)
    ChangeRelation (FK target/kind switch)

Rename detection heuristic

  Field: same type + name-similarity Levenshtein greater than 0.7
  Model: 0.5 * field-shape-Jaccard + 0.35 * field-name-Jaccard +
         0.15 * name-Levenshtein-similarity
  Both use "top-of-both" matching so ambiguous pairs are held back.
  Confidence is returned on every rename event for a confirm-UI later.

Git wrapper (src/git.ts)

  execFile-based, no shell interpolation
  gitLogFollow / blobShaFor / gitShowFile
  BlobCache LRU keyed on BLOB SHA (not commit SHA) so history walks
  with no models.py churn share parsed snapshots for free

Command djangoOrmLens.showDiff

  If more than one models.py, QuickPick to choose target
  Two QuickPicks for from-commit / to-commit (last 200 commits touching
    the file, most-recent first)
  Diff runs on parsed models, not text
  Result opens as an untitled markdown buffer with commit metadata header

Tests

  13 new schema-diff snapshot cases including rename detection, option
  whitelist, ordering guarantees, and markdown export.
  Full suite 66/66 green.

Deferred to follow-up

  ER-diagram overlay (Mermaid classDef added/dropped/renamed)
  Rename confirmation chips in a webview drawer
  CLI parity (django-orm-lens diff --from <ref> --to <ref> --json)
FROWNINGdev added a commit that referenced this pull request Jul 23, 2026
Right-click a field or model, pick a template, get a Django ORM snippet
inserted at the cursor (with tab-stops) or in a fresh untitled Python
buffer if no editor is focused.

Blueprint distilled from proven prior art (research-first):

  DataGrip / DBeaver        Right-click submenu of templates; output
                             goes into an editable buffer, not
                             clipboard-silently. Copied verbatim.
  Prisma Studio             Best filter UX but zero code export.
                             Their gap is exactly what we fill.
  django-silk               Cluster N+1: pick a template on an FK,
                             we automatically add .select_related.
  Django docs               Grammar gotchas we honour:
                             related_name overrides <lower>_set
                             .select_related for FK/O2O only
                             .prefetch_related for M2M/reverse FK
                             .values().distinct() before .distinct()

MVP templates (grouped filter -> perf -> aggregate -> projection)

  .filter(field=<value>)              scalar or FK; FK auto-appends
                                      .select_related for the FK
  .select_related(fk)                 focused N+1 fix for FK/O2O
  .prefetch_related(m2m)              M2M fetch batching
  .annotate(rel_count=Count(access))  reverse-FK count with order_by;
                                      honours related_name
  .values(field).distinct()           distinct scalar values
  .only(field)                        column pruning
  .objects.all()                      base QuerySet for a model target

Insertion strategy (VS Code convention)

  active Python editor  SnippetString insertSnippet at cursor with
                        tab-stops on placeholders (${1:value})
  no active editor      new untitled Python doc with a `from ... import`
                        boilerplate line, then insert the snippet

Deferred to follow-up (v2 of this feature)

  Multi-hop path picker in the ER-diagram webview
  Prefetch(...) with a nested filter
  Q() OR-composition toggle
  Subquery + OuterRef wizard

Tests: +8 pure snapshot cases (85/85 total, up from 77). Full suite
green, tsc clean.

All five WOW features from Discussion #27 now shipped:

  #1 Impact Analysis    53e51f2
  #2 Query Builder      HEAD
  #3 Inline Fixes       b99899a
  #4 Factory Generator  473d3ff
  #5 Schema Diff        040f2bd
Repository owner deleted a comment from coderabbitai Bot Jul 25, 2026
Repository owner deleted a comment from coderabbitai Bot Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support edge labels for OneToOneField / M2M through models

2 participants