Skip to content

templates — template coherence

ID: templates
Since: 0.6.0
Order: 45 (between forms_meta and urls)
Needs DB: no
Severity: WARNING (missing static) / ERROR (unknown URL name, template syntax)

What it catches

Three silent-problem patterns in .html files:

  1. {% url 'name' %} pointing at a URL name that the resolver doesn't know about. Django only raises NoReverseMatch at render time, so a typo in a template behind an auth wall rides in main until a user finally hits the page.
  2. {% static 'path/to/asset.ext' %} that no staticfiles finder resolves. Broken asset paths don't surface at collectstatic time unless you read its verbose output.
  3. Malformed template syntax. TemplateSyntaxError at load time, usually introduced by a half-finished edit. ./manage.py check doesn't crawl templates.

What it deliberately does not do

Context-variable resolution. A template's {{ foo.bar }} can only be judged against a specific view's context, and wiring that plumbing is a can of worms that belongs in a views + coverage integration — not here.

Example finding

templates — 2 finding(s)
┌─────────┬─────────────────────┬───────────────────────────────┬────────────────────────────────────────────────────────┐
│ Sev     │ Rule                │ Location                      │ Message                                                │
├─────────┼─────────────────────┼───────────────────────────────┼────────────────────────────────────────────────────────┤
│ ERROR   │ templates.url.unknown│ apps/core/templates/nav.html:12 │ {% url 'mandat-detail' %} — no URL pattern registered  │
│         │                     │                               │ under this name.                                        │
│         │                     │                               │ → Check for a typo, missing include(), or an app whose │
│         │                     │                               │   urls.py isn't wired in ROOT_URLCONF.                  │
│ WARNING │ templates.static.missing│ apps/core/templates/base.html:4 │ {% static 'img/logo-xx.svg' %} — no staticfiles finder │
│         │                     │                               │ resolves this path.                                     │
└─────────┴─────────────────────┴───────────────────────────────┴────────────────────────────────────────────────────────┘

Configuration

[tool.django-doctor.templates]
# Glob patterns of template paths (relative to the template root) to skip.
skip = ["vendor/*", "partials/_debug.html"]
# Set to false if your project doesn't use Django's staticfiles finders.
check_static = true

Scope

  • Templates under each first-party app's templates/ directory.
  • Templates under TEMPLATES[...]["DIRS"] entries that live inside settings.BASE_DIR — third-party template dirs are skipped.
  • Namespaced URL names (foo:bar) are matched both as-is and against the bare tail, so {% url 'mandat:detail' %} resolves when only detail is registered.