Route Debug Middleware
RouteDebugMiddleware intercepts 404 responses in DEBUG mode and logs detailed context — the request path, HTTP method, referer, and URL patterns that partially matched. This makes it easy to diagnose router misconfiguration where routes end up under the wrong API group prefix.
When to Use
The most common symptom is a 404 that “shouldn’t” happen:
GET /api/projects/123/characters/ → 404 Not Found…when the route actually lives at:
/api/scenes/projects/123/characters/This happens with nested DRF routers where one router’s routes leak into another group’s prefix. Without this middleware you only see the 404 — the middleware shows which patterns almost matched.
Enabling
Auto-enabled by django-cfg when DEBUG=True. You can also add it manually:
# config.py
MIDDLEWARE = [
...
"django_cfg.middleware.route_debug.RouteDebugMiddleware",
...
]Or via django-cfg config:
from django_cfg import DjangoConfig
class MyConfig(DjangoConfig):
debug_log_404: bool = True # default True when DEBUG=TrueLog Output
When a 404 occurs in DEBUG mode, the middleware writes to the django_cfg.debug_404 logger:
WARNING django_cfg.debug_404:
404 GET /api/projects/123/characters/
Referer: http://localhost:3000/projects/123
Partial matches (3 patterns share prefix):
api/projects/<pk>/ → project-detail
api/projects/<pk>/members/ → project-members
api/scenes/projects/<pk>/characters/ → character-listThe last line reveals the actual route — the router leaked the path into the wrong group prefix.
Log Configuration
To see output, configure the logger in your settings:
LOGGING = {
"version": 1,
"handlers": {
"console": {"class": "logging.StreamHandler"},
},
"loggers": {
"django_cfg.debug_404": {
"handlers": ["console"],
"level": "WARNING",
},
},
}How It Works
The middleware uses a two-pass approach:
- Response hook — checks
response.status_code == 404after the view runs - Exception hook — catches
Http404raised directly by views - Pattern scan — flattens the entire URL resolver tree and finds patterns whose first 2 path segments overlap with the 404 path
# Pseudocode
path_parts = path.split("/") # ["api", "projects", "123", "characters"]
for pattern, view_name in all_url_patterns:
pattern_parts = pattern.split("/") # strip regex groups
if path_parts[:2] == pattern_parts[:2]: # share first 2 segments
candidates.append((pattern, view_name))Up to 10 candidates are logged.
Disabling
The middleware is a no-op when DEBUG=False — safe to leave in MIDDLEWARE for production deploys.
To disable in development:
class MyConfig(DjangoConfig):
debug_log_404: bool = FalseTAGS: middleware, debugging, 404, url-routing, drf DEPENDS_ON: [async-orm]