django-uvtmap — see which view and template every URL in your project actually hits

If you’ve worked on a Django project past a certain size, you know the feeling: you’re staring at a URL, and you genuinely don’t know — without opening three files and reading branch logic by hand — which template will actually render when a request hits it. Maybe it’s a DetailView relying on naming conventions. Maybe it’s a function view with a try/except that renders three different templates depending on what goes wrong. Maybe someone added a redirect six months ago and the “template” is actually just a bounce to another URL.

PyCharm has a version of this, but it’s locked inside the IDE. I couldn’t find anything else — so I built django-uvtmap, a management command that maps every URL in your project to its view, and every view to the templates it can render, including the conditions under which each one fires.

pip install django-uvtmap

Add "uvtmap" to INSTALLED_APPS, then:

python manage.py uvtmap --html map.html

This is the command that matters — it opens an interactive, self-contained HTML page. No server, no build step. Just three columns — URLs, views, templates — wired together, with hovering tracing a full chain end to end and clicking pinning a node open for inspection.

What it actually does

  • Full URL → view → template mapping, for function-based and class-based views alike
  • Render conditions — the tool walks the AST upward from every render() call and
    reconstructs the branch path that leads to it: if/elif/else (correctly negated
    depending on which branch you’re in), try/except, match/case, loops. So instead
    of just “this view renders dashboard.html,” you get “renders dash_compact.html if
    mode == 'compact', dash_full.html otherwise, dash_default.html on KeyError.”
  • Redirect trackingredirect(), reverse(), reverse_lazy(), and literal-path
    HttpResponseRedirect() calls are followed back to their target URL, so a view that
    redirects instead of rendering still shows up as a real edge in the map, not a dead end.
  • Missing-template detection — every statically-resolvable template name is checked
    against Django’s actual configured template loaders. If it doesn’t exist, the map
    flags it. This accidentally makes the tool double as a linter — I’ve caught stale
    template references in my own project just by running it.
  • CBV support — explicit template_name, the generic-view default naming
    convention (DetailView on Post correctly resolves to blog/post_detail.html
    even with nothing set explicitly), and render() calls inside a class’s own methods.
  • --app and --list-apps — scope the map to a single app, project or third-party
    (yes, it’ll map django-allauth’s URLs if you want to see them), or list every app
    with URL-routed views before committing to a full run.
  • --json output for scripting — It could be used in CI to fail a build if any template
    comes back "exists": false.

What it deliberately doesn’t do

No requests are made and no views are executed — everything is static analysis. That means a few honest limitations:

  • Implicit fallthrough (if x: return render(a) followed by return render(b)) shows
    b as “always rendered” instead of “if not x” — that needs real control-flow
    analysis, not just an AST walk.
  • Template names built at runtime (an f-string, a variable) can’t be resolved — the
    tool shows you the expression instead and marks it dynamic.
  • get_template_names() overrides on CBVs aren’t traced yet.

These are documented in the README, and I’d rather ship an honest v0.1.0 than an
overclaimed one.

Try it

pip install django-uvtmap

It’s a fresh release — genuinely interested in bug reports, edge cases I haven’t hit
in my own projects, and feature requests. If your project has a URL pattern this
doesn’t handle correctly, that’s exactly the kind of thing I want to know about.


Built in collaboration with Claude (Anthropic) — concept, prototype, feature direction, and every design call along the way are mine; a lot of the implementation was AI-assisted. Said plainly because I’d rather be upfront about it than have someone read the code and wonder.

2 Likes