djangomap 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- djangomap-0.1.0/LICENSE +21 -0
- djangomap-0.1.0/MANIFEST.in +4 -0
- djangomap-0.1.0/PKG-INFO +453 -0
- djangomap-0.1.0/README.md +420 -0
- djangomap-0.1.0/djangomap/__init__.py +6 -0
- djangomap-0.1.0/djangomap/analysis.py +189 -0
- djangomap-0.1.0/djangomap/cli.py +81 -0
- djangomap-0.1.0/djangomap/export.py +127 -0
- djangomap-0.1.0/djangomap/render.py +36 -0
- djangomap-0.1.0/djangomap/scanner.py +625 -0
- djangomap-0.1.0/djangomap/tailwind.css +1 -0
- djangomap-0.1.0/djangomap/template.html +972 -0
- djangomap-0.1.0/djangomap.egg-info/PKG-INFO +453 -0
- djangomap-0.1.0/djangomap.egg-info/SOURCES.txt +17 -0
- djangomap-0.1.0/djangomap.egg-info/dependency_links.txt +1 -0
- djangomap-0.1.0/djangomap.egg-info/entry_points.txt +2 -0
- djangomap-0.1.0/djangomap.egg-info/top_level.txt +1 -0
- djangomap-0.1.0/pyproject.toml +54 -0
- djangomap-0.1.0/setup.cfg +4 -0
djangomap-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 djangomap contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
djangomap-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djangomap
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn any Django project into an interactive HTML architecture diagram: models, views, URLs, Celery tasks, signals — plus a health report.
|
|
5
|
+
Author-email: Armin <arminyarali1@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/selzman/djangomap
|
|
8
|
+
Project-URL: Repository, https://github.com/selzman/djangomap
|
|
9
|
+
Project-URL: Issues, https://github.com/selzman/djangomap/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/selzman/djangomap/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: django,diagram,visualization,architecture,erd,celery,static-analysis,ast,documentation,graph,code-quality,flowchart
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Documentation
|
|
25
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
26
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
27
|
+
Classifier: Topic :: Utilities
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+
# djangomap
|
|
37
|
+
|
|
38
|
+
**Turn any Django project into a beautiful, interactive architecture diagram.**
|
|
39
|
+
|
|
40
|
+
Models · Views · URLs · Celery tasks · Beat schedules · Signals · Middleware · Admin · Serializers
|
|
41
|
+
|
|
42
|
+
<p>
|
|
43
|
+
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-7ee787.svg?style=flat-square">
|
|
44
|
+
<img alt="Python 3.9+" src="https://img.shields.io/badge/Python-3.9%2B-79c0ff.svg?style=flat-square&logo=python&logoColor=white">
|
|
45
|
+
<img alt="Django" src="https://img.shields.io/badge/Django-any%20version-092E20.svg?style=flat-square&logo=django&logoColor=white">
|
|
46
|
+
<img alt="Zero dependencies" src="https://img.shields.io/badge/dependencies-zero-d2a8ff.svg?style=flat-square">
|
|
47
|
+
</p>
|
|
48
|
+
<p>
|
|
49
|
+
<img alt="Output: single HTML file" src="https://img.shields.io/badge/output-single%20HTML%20file-ffa657.svg?style=flat-square&logo=html5&logoColor=white">
|
|
50
|
+
<img alt="Works offline" src="https://img.shields.io/badge/works-offline-56d4dd.svg?style=flat-square">
|
|
51
|
+
<img alt="Responsive" src="https://img.shields.io/badge/UI-responsive-ff9ecd.svg?style=flat-square">
|
|
52
|
+
<img alt="Static analysis: AST" src="https://img.shields.io/badge/analysis-AST%20only-f0c674.svg?style=flat-square">
|
|
53
|
+
<img alt="Health checks: 16" src="https://img.shields.io/badge/health%20checks-16-ff7b72.svg?style=flat-square">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
[Why](#why) · [Quick start](#quick-start) · [The three tabs](#the-three-tabs) · [CLI](#cli-reference) · [CI](#ci-integration) · [How it works](#how-it-works) · [License](#license)
|
|
57
|
+
|
|
58
|
+
<img src="docs/images/apps-overview.png" alt="djangomap Apps view" width="100%">
|
|
59
|
+
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Why
|
|
65
|
+
|
|
66
|
+
You join a Django codebase with 40 apps. Which view serves `/checkout/`? What fires
|
|
67
|
+
`capture_payment`? Which models does the admin actually expose? Answering these means
|
|
68
|
+
grepping across dozens of files.
|
|
69
|
+
|
|
70
|
+
`djangomap` reads your project **without importing it** and produces a **single HTML
|
|
71
|
+
file** that answers those questions visually — plus it flags N+1 risks, circular app
|
|
72
|
+
dependencies and missing `related_name`s along the way.
|
|
73
|
+
|
|
74
|
+
| | |
|
|
75
|
+
|---|---|
|
|
76
|
+
| **Zero setup** | No database, no `DJANGO_SETTINGS_MODULE`, no installing the target project's dependencies |
|
|
77
|
+
| **Zero dependencies** | Pure Python standard library — `ast` and nothing else |
|
|
78
|
+
| **Fully offline** | Tailwind is pre-compiled and inlined; the page makes no network requests |
|
|
79
|
+
| **One file** | Email it, commit it, attach it to a PR, open it from disk |
|
|
80
|
+
| **Responsive** | Works on desktop, tablet and phone |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install -e ./djangomap
|
|
88
|
+
djangomap /path/to/your/project -o diagram.html
|
|
89
|
+
open diagram.html
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
That's it. For the full experience, add source links:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
djangomap . \
|
|
96
|
+
--title "My Shop" \
|
|
97
|
+
--editor vscode \
|
|
98
|
+
--repo-url https://github.com/me/myshop \
|
|
99
|
+
--branch main \
|
|
100
|
+
-o diagram.html
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or use it as a library:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from djangomap import scan_project, render_html
|
|
107
|
+
from djangomap.analysis import analyse
|
|
108
|
+
|
|
109
|
+
proj = scan_project("/path/to/project")
|
|
110
|
+
data = proj.to_dict()
|
|
111
|
+
data["health"] = analyse(proj)
|
|
112
|
+
|
|
113
|
+
render_html(data, "diagram.html")
|
|
114
|
+
print(proj.stats()) # {'model': 8, 'view': 8, 'url': 12, ...}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## The three tabs
|
|
120
|
+
|
|
121
|
+
### 1 · Apps — what is in each app
|
|
122
|
+
|
|
123
|
+
Every app becomes its own board with a dedicated colour. Inside, cards are grouped by
|
|
124
|
+
kind and show a useful summary line: a model's field count, a URL's handler and
|
|
125
|
+
`name=`, a view's base class, a task's argument signature, a beat entry's schedule.
|
|
126
|
+
|
|
127
|
+
Relationship wires are routed **through the gutters between boards**, so they never
|
|
128
|
+
cut across cards — including relations that span apps.
|
|
129
|
+
|
|
130
|
+
<img src="docs/images/apps-overview.png" alt="Apps overview" width="100%">
|
|
131
|
+
|
|
132
|
+
**Click any card** to open the detail panel: full field list with `on_delete` and
|
|
133
|
+
`related_name`, the `Meta` class, properties, methods, cyclomatic complexity, detected
|
|
134
|
+
issues, and every relation as a clickable link so you can walk the graph.
|
|
135
|
+
|
|
136
|
+
<img src="docs/images/detail-panel.png" alt="Detail panel" width="100%">
|
|
137
|
+
|
|
138
|
+
Selecting a node also **dims everything unrelated**, leaving just its neighbourhood lit:
|
|
139
|
+
|
|
140
|
+
<img src="docs/images/focus-mode.png" alt="Focus mode" width="100%">
|
|
141
|
+
|
|
142
|
+
> Drag a board header to rearrange · scroll to zoom · `F` to fit · `Esc` to clear
|
|
143
|
+
> · collapse individual boards or all at once for a bird's-eye view
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 2 · Flow — how the system fits together
|
|
148
|
+
|
|
149
|
+
Four architecture views, switchable from the toolbar.
|
|
150
|
+
|
|
151
|
+
#### Request Lifecycle
|
|
152
|
+
|
|
153
|
+
The full path of an HTTP request through parallel lanes: Middleware → URLconf → View
|
|
154
|
+
→ Serializer/Form → Model, with a separate lane for async Celery work.
|
|
155
|
+
|
|
156
|
+
<img src="docs/images/flow-request.png" alt="Request lifecycle" width="100%">
|
|
157
|
+
|
|
158
|
+
#### App Dependencies
|
|
159
|
+
|
|
160
|
+
Which apps import which, derived from real `import` statements, annotated with
|
|
161
|
+
reference counts. Circular dependencies show up immediately.
|
|
162
|
+
|
|
163
|
+
<img src="docs/images/flow-appdeps.png" alt="App dependencies" width="100%">
|
|
164
|
+
|
|
165
|
+
#### Celery Pipeline
|
|
166
|
+
|
|
167
|
+
Producers (views, signals, beat entries, management commands) → Broker → Workers →
|
|
168
|
+
Result backend, including task-to-task chains.
|
|
169
|
+
|
|
170
|
+
<img src="docs/images/flow-celery.png" alt="Celery pipeline" width="100%">
|
|
171
|
+
|
|
172
|
+
#### Data Model
|
|
173
|
+
|
|
174
|
+
A full ERD: every model with its fields, and the FK / M2M / O2O relations between
|
|
175
|
+
them, grouped by app.
|
|
176
|
+
|
|
177
|
+
<img src="docs/images/flow-erd.png" alt="ERD" width="100%">
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### 3 · Health — what is wrong
|
|
182
|
+
|
|
183
|
+
A 0–100 score plus issues grouped by check. **Click any issue to jump straight to that
|
|
184
|
+
card** in the Apps tab. Cards with problems get a coloured dot.
|
|
185
|
+
|
|
186
|
+
<img src="docs/images/health.png" alt="Health tab" width="100%">
|
|
187
|
+
|
|
188
|
+
Below the issue list, a per-app breakdown shows where the debt is concentrated:
|
|
189
|
+
|
|
190
|
+
<img src="docs/images/health-perapp.png" alt="Per-app health" width="100%">
|
|
191
|
+
|
|
192
|
+
#### Checks
|
|
193
|
+
|
|
194
|
+
| Check | Severity | Meaning |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `view.n_plus_one` | 🔴 error | Model has FKs but the view never calls `select_related`/`prefetch_related` |
|
|
197
|
+
| `url.no_view` | 🔴 error | URL is not wired to any known view |
|
|
198
|
+
| `app.cycle` | 🔴 error | Circular dependency between apps |
|
|
199
|
+
| `complexity.high` | 🟡 warn | Cyclomatic complexity ≥ 10 (error at ≥ 18) |
|
|
200
|
+
| `model.no_str` | 🟡 warn | Model has no `__str__`, so it renders as `Object (1)` in admin |
|
|
201
|
+
| `model.no_ordering` | 🟡 warn | Used in a `ListView` but has no `Meta.ordering` → unstable pagination |
|
|
202
|
+
| `url.no_name` | 🟡 warn | No `name=`, so it cannot be used with `reverse()` |
|
|
203
|
+
| `view.no_perm` | 🟡 warn | DRF view without `permission_classes` |
|
|
204
|
+
| `task.orphan` | 🟡 warn | Task is never called — no `delay()` and no beat schedule |
|
|
205
|
+
| `signal.no_sender` | 🟡 warn | `@receiver` without `sender` fires for *every* model |
|
|
206
|
+
| `model.fk_no_related` | 🔵 info | `ForeignKey` without `related_name` |
|
|
207
|
+
| `model.orphan` | 🔵 info | No view / serializer / admin references this model |
|
|
208
|
+
| `model.no_indexes` | 🔵 info | Many fields but no `db_index` anywhere |
|
|
209
|
+
| `task.no_retry` | 🔵 info | No `max_retries` / `autoretry_for` |
|
|
210
|
+
| `task.no_bind_retry` | 🔵 info | Uses `self.retry` but not declared with `bind=True` |
|
|
211
|
+
| `view.unauth` | 🔵 info | No `login_required` / permission mixin |
|
|
212
|
+
|
|
213
|
+
> `view.unauth` and `model.orphan` are heuristic and can be noisy on real projects,
|
|
214
|
+
> which is why they are `info` and barely affect the score.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Responsive
|
|
219
|
+
|
|
220
|
+
The whole UI adapts down to a 320px phone.
|
|
221
|
+
|
|
222
|
+
<div align="center">
|
|
223
|
+
<img src="docs/images/mobile-apps.png" alt="Mobile" width="30%">
|
|
224
|
+
<img src="docs/images/mobile-drawer.png" alt="Mobile drawer" width="30%">
|
|
225
|
+
<img src="docs/images/mobile-sheet.png" alt="Mobile detail sheet" width="30%">
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
| Breakpoint | Behaviour |
|
|
229
|
+
|---|---|
|
|
230
|
+
| ≥ 1536px | Kind chips inline in the header |
|
|
231
|
+
| < 1536px | Kind filters move into the drawer via the **Kinds** button |
|
|
232
|
+
| ≥ 1024px | Sidebar always visible |
|
|
233
|
+
| < 1024px | Sidebar becomes an off-canvas drawer behind ☰ ; detail panel becomes a bottom sheet and diagrams auto-fit above it |
|
|
234
|
+
| < 760px | Flow layers stack vertically and fit to width |
|
|
235
|
+
|
|
236
|
+
Pinch-to-zoom and touch panning work on both canvases. Tablet reflows to two columns:
|
|
237
|
+
|
|
238
|
+
<img src="docs/images/tablet-apps.png" alt="Tablet" width="100%">
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## CLI reference
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
djangomap [path] [options]
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
| Option | Description |
|
|
249
|
+
|---|---|
|
|
250
|
+
| `path` | Project root (default: `.`) |
|
|
251
|
+
| `-o, --out FILE` | Output HTML file (default: `djangomap.html`) |
|
|
252
|
+
| `--title TEXT` | Diagram title |
|
|
253
|
+
| `--json FILE` | Also dump the raw graph as JSON |
|
|
254
|
+
| `--editor NAME` | `vscode` · `vscode-insiders` · `pycharm` · `none` |
|
|
255
|
+
| `--repo-url URL` | e.g. `https://github.com/me/proj` — enables "view on remote" links |
|
|
256
|
+
| `--branch NAME` | Branch for `--repo-url` (default: `main`) |
|
|
257
|
+
| `--format FMT` | Print a text diagram: `mermaid-erd` · `mermaid-flow` · `dot` |
|
|
258
|
+
| `--markdown FILE` | Write a Markdown report with an embedded Mermaid ERD |
|
|
259
|
+
| `--fail-on LEVEL` | Exit 1 if issues at `error` / `warn` / `info` exist |
|
|
260
|
+
| `--no-health` | Skip the health analysis |
|
|
261
|
+
|
|
262
|
+
### Text exports
|
|
263
|
+
|
|
264
|
+
Paste straight into a README or PR comment:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
djangomap . --format mermaid-erd # GitHub renders this natively
|
|
268
|
+
djangomap . --format mermaid-flow
|
|
269
|
+
djangomap . --format dot | dot -Tsvg -o graph.svg
|
|
270
|
+
djangomap . --markdown report.md
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
<details>
|
|
274
|
+
<summary><b>Example Mermaid output</b></summary>
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
erDiagram
|
|
278
|
+
Product {
|
|
279
|
+
Char title
|
|
280
|
+
Decimal price
|
|
281
|
+
ForeignKey category FK
|
|
282
|
+
ManyToMany tags FK
|
|
283
|
+
}
|
|
284
|
+
Product ||--o{ Category : "category"
|
|
285
|
+
Product }o--o{ Tag : "tags"
|
|
286
|
+
```
|
|
287
|
+
</details>
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## CI integration
|
|
292
|
+
|
|
293
|
+
Fail the build when architectural errors appear:
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# .github/workflows/architecture.yml
|
|
297
|
+
name: Architecture
|
|
298
|
+
on: [push, pull_request]
|
|
299
|
+
|
|
300
|
+
jobs:
|
|
301
|
+
djangomap:
|
|
302
|
+
runs-on: ubuntu-latest
|
|
303
|
+
steps:
|
|
304
|
+
- uses: actions/checkout@v4
|
|
305
|
+
- uses: actions/setup-python@v5
|
|
306
|
+
with: { python-version: "3.12" }
|
|
307
|
+
- run: pip install -e ./djangomap
|
|
308
|
+
- run: djangomap . --fail-on error -o diagram.html
|
|
309
|
+
- uses: actions/upload-artifact@v4
|
|
310
|
+
if: always()
|
|
311
|
+
with:
|
|
312
|
+
name: architecture-diagram
|
|
313
|
+
path: diagram.html
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Every PR then carries a downloadable, up-to-date diagram of the codebase.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Keyboard shortcuts & permalinks
|
|
321
|
+
|
|
322
|
+
| Key | Action |
|
|
323
|
+
|---|---|
|
|
324
|
+
| `1` `2` `3` | Switch to Apps / Flow / Health |
|
|
325
|
+
| `F` | Fit the current view |
|
|
326
|
+
| `Esc` | Clear the selection |
|
|
327
|
+
|
|
328
|
+
The active tab, flow view, selected node and every filter are encoded in the URL
|
|
329
|
+
`#hash`. Send the link to a teammate and they land on exactly the same view.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## What gets extracted
|
|
334
|
+
|
|
335
|
+
<details open>
|
|
336
|
+
<summary><b>Models</b></summary>
|
|
337
|
+
|
|
338
|
+
Every field with its type, `max_length`, `null` / `blank` / `unique` / `db_index` /
|
|
339
|
+
`primary_key`, `on_delete`, `related_name`, `default`, `help_text`, whether it has
|
|
340
|
+
`choices` — plus the `Meta` class, `@property` methods, custom managers and
|
|
341
|
+
cyclomatic complexity.
|
|
342
|
+
</details>
|
|
343
|
+
|
|
344
|
+
<details>
|
|
345
|
+
<summary><b>Views</b></summary>
|
|
346
|
+
|
|
347
|
+
CBV and FBV, HTTP methods (from `get`/`post` methods or `@api_view`), `model`,
|
|
348
|
+
`queryset`, `serializer_class`, `form_class`, `template_name`, `permission_classes`,
|
|
349
|
+
`authentication_classes`, `paginate_by`, `lookup_field`, `filter_backends`, and the
|
|
350
|
+
list of URLs that reach the view.
|
|
351
|
+
</details>
|
|
352
|
+
|
|
353
|
+
<details>
|
|
354
|
+
<summary><b>URLs</b></summary>
|
|
355
|
+
|
|
356
|
+
`path()` / `re_path()` / `url()` / `include()`, path converters like `<int:pk>`,
|
|
357
|
+
`name=`, extra kwargs, and DRF router `register()` calls.
|
|
358
|
+
</details>
|
|
359
|
+
|
|
360
|
+
<details>
|
|
361
|
+
<summary><b>Celery</b></summary>
|
|
362
|
+
|
|
363
|
+
`@shared_task`, `@app.task` and method-level tasks with their full argument
|
|
364
|
+
signature and options (`bind`, `max_retries`, `queue`, `rate_limit`, `autoretry_for`,
|
|
365
|
+
`acks_late`, `time_limit`, …), the `delay()` / `apply_async()` call chain, and every
|
|
366
|
+
`CELERY_BEAT_SCHEDULE` entry wired to its task.
|
|
367
|
+
</details>
|
|
368
|
+
|
|
369
|
+
<details>
|
|
370
|
+
<summary><b>Everything else</b></summary>
|
|
371
|
+
|
|
372
|
+
Signals (type + `sender`), middleware, management commands, forms, serializers
|
|
373
|
+
(`Meta.model`, `fields`, `read_only_fields`), admin classes (`list_display`,
|
|
374
|
+
`list_filter`, `search_fields`, registrations), and settings: `INSTALLED_APPS`,
|
|
375
|
+
`MIDDLEWARE`, `AUTH_USER_MODEL`, `ROOT_URLCONF`, broker and result backend.
|
|
376
|
+
</details>
|
|
377
|
+
|
|
378
|
+
### Relationship types
|
|
379
|
+
|
|
380
|
+
| Wire | Meaning |
|
|
381
|
+
|---|---|
|
|
382
|
+
| `fk` `m2m` `o2o` | Model relations |
|
|
383
|
+
| `routes` | URL → View |
|
|
384
|
+
| `calls` | `task.delay()` / `apply_async()` |
|
|
385
|
+
| `schedules` | Beat entry → task |
|
|
386
|
+
| `uses` | View → serializer / form / model |
|
|
387
|
+
| `queries` | ORM access inside a view or task |
|
|
388
|
+
| `manages` | Admin class → model |
|
|
389
|
+
| `listens` | Signal receiver → sender model |
|
|
390
|
+
| `inherits` | Class inheritance within the project |
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## How it works
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
scanner.py walks the tree, parses each .py with ast, emits nodes + edges
|
|
398
|
+
analysis.py runs 16 checks over that graph, scores the project
|
|
399
|
+
export.py renders Mermaid / DOT / Markdown
|
|
400
|
+
render.py inlines the graph JSON + pre-built Tailwind into one HTML file
|
|
401
|
+
cli.py argument parsing and orchestration
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Because everything is AST-based, **your code is never executed** — safe to point at
|
|
405
|
+
an unfamiliar repository. The trade-off is that dynamically constructed URLconfs or
|
|
406
|
+
programmatically generated models won't be seen. For those, the graph is a very good
|
|
407
|
+
approximation rather than a perfect runtime reflection.
|
|
408
|
+
|
|
409
|
+
Migrations, `node_modules`, virtualenvs, caches and static dirs are skipped.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Development
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
python -m djangomap.cli ./sample_shop -o demo.html # run against the bundled sample
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The Tailwind stylesheet is pre-built at `djangomap/tailwind.css`. Rebuild it after
|
|
420
|
+
editing `template.html`:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npx tailwindcss -c tailwind.config.js -i tw.css -o djangomap/tailwind.css --minify
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The repo ships a `sample_shop/` Django project (4 apps, 8 models, 6 tasks, DRF,
|
|
427
|
+
Celery Beat, a management command) used for the screenshots above.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Limitations
|
|
432
|
+
|
|
433
|
+
- Dynamic URLconfs and runtime-generated models are invisible to static analysis
|
|
434
|
+
- Third-party apps are only mapped if they live inside the scanned tree
|
|
435
|
+
- `related_name` reverse accessors that Django creates implicitly are not inferred
|
|
436
|
+
- The N+1 check is a heuristic: it flags missing `select_related` but cannot know
|
|
437
|
+
whether the template actually traverses the relation
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## License
|
|
442
|
+
|
|
443
|
+
Released under the [MIT License](LICENSE) — free for personal and commercial use.
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
Copyright (c) 2026 djangomap contributors
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
<div align="center">
|
|
452
|
+
<sub>Built with Python's <code>ast</code>, Tailwind CSS and hand-rolled SVG. No runtime dependencies.</sub>
|
|
453
|
+
</div>
|