Performance Dashboard
The middleware’s per-request analysis also feeds a local web dashboard: a live, self-refreshing view of every analyzed request with its grade, query metrics, detected issues (N+1, missing select_related, …), recommendations, and slow queries with EXPLAIN insights.
It is a development tool in the spirit of django-debug-toolbar, but request-history oriented: browse your app in one tab, watch the timeline fill up in another, click any request to see exactly what to fix and where (file:line locations included).
Three lines on top of the middleware you already have:
INSTALLED_APPS = [ # ... 'dbcrust.django', # template discovery for the dashboard]
MIDDLEWARE = [ 'dbcrust.django.PerformanceAnalysisMiddleware', # ...]from django.conf import settingsfrom django.urls import include, path
if settings.DEBUG: urlpatterns += [path('__dbcrust__/', include('dbcrust.django.urls'))]Open http://localhost:8000/__dbcrust__/ and browse your app — requests appear as they happen.
Any prefix works; __dbcrust__ is just a convention. No staticfiles configuration, build step, or CDN is needed: the UI is plain Django templates driven by a vendored htmx (the request list polls every 2 seconds).
What you see
Section titled “What you see”Request list (newest first) — grade badge (A–F), time, method + path, query count, DB time, request time, and issue counts split by severity. Header stats aggregate the buffer: total requests, requests with issues, average queries, and average DB time.
Detail pane — click a request:
- Metrics: queries, DB time, request time, duplicates, and the SELECT/INSERT/UPDATE/DELETE breakdown
- Critical / Warnings / Hints — each detected pattern with its description, a concrete recommendation (e.g.
select_related('author')), a code suggestion, and clickablefile:linelocations - Slow queries — SQL, duration, tables, and EXPLAIN insights (plan type, rows examined, suggested fix) when the EXPLAIN integration is active
The dashboard’s own polling requests are recognized (by URL namespace) and excluded from analysis, so it never pollutes its own data.
Investigate with AI
Section titled “Investigate with AI”When a request has slow queries or flagged issues, the detail pane shows a 🤖 Investigate with AI button. One click hands the request’s slow queries, detected issues, and your Django models to the AI assistant, which investigates the live database read-only (running EXPLAIN, inspecting indexes) and returns a Django-aware analysis — root cause plus the concrete fix (select_related / db_index / …) and the underlying SQL.
It can reuse your existing AI setup (dbcrust → \ai setup). In Docker, a mounted Codex login (codex login on the host, mounted to the Django user’s ~/.codex/auth.json) is enough for ChatGPT-subscription auth. Notes:
- The investigation runs in a background thread and the panel streams the agent’s progress live (the tools it runs, rows seen) via htmx polling — the dashboard stays responsive the whole time, then swaps in the final analysis. A full investigation takes ~30s.
- It runs read-only queries only — writes, DDL, and side-effecting statements are rejected. (Best-effort SQL inspection, not a hard sandbox; for sensitive databases use a read-only role or replica.)
- Requires API-key or ChatGPT-subscription auth (same as
??/???in the CLI). If the assistant isn’t configured, the panel shows a short error hint instead. - It can reuse the normal DBCrust config directory (
DBCRUST_CONFIG_DIR=/path/to/.config/dbcrust), but Dockerized Django can also be config-free: mount~/.codexfrom the host to the container user’s~/.codexread-only, and the Django AI entrypoints auto-detect it. - It connects to the
defaultdatabase alias by default; override withDBCRUST_AI_DATABASE = "<alias>"in settings. - Privacy: this feature can send captured SQL, model/source context, query plans, and bounded result rows to the configured AI provider. Review the AI privacy notes before enabling it on sensitive data.
Configuration
Section titled “Configuration”All dashboard keys in DBCRUST_PERFORMANCE_ANALYSIS (defaults shown):
DBCRUST_PERFORMANCE_ANALYSIS = { 'DASHBOARD_ENABLED': True, # record analyzed requests for the dashboard 'DASHBOARD_MAX_REQUESTS': 100, # history size (oldest pruned first) 'DASHBOARD_PERSIST': True, # keep history across restarts (SQLite file) 'DASHBOARD_DB_PATH': None, # None → BASE_DIR/.dbcrust/dashboard.sqlite3}Unlike console reports — which only fire on issues or threshold breaches — the dashboard records every analyzed request, healthy ones included, so the timeline is complete.
Storage
Section titled “Storage”History is persisted by default to a dedicated SQLite file at BASE_DIR/.dbcrust/dashboard.sqlite3, so it survives runserver autoreloads and restarts — fix the N+1 the dashboard showed you, save, and the “before” requests are still there to compare against. Add the directory to your project’s .gitignore:
.dbcrust/Worth knowing:
- This is not your project database — no models, no migrations, no
DATABASESentry. It’s a self-managed file (WAL mode, capped atDASHBOARD_MAX_REQUESTS, schema migrated by drop-and-recreate since history is disposable). - Multi-process servers work: gunicorn workers all write to the same file, so the dashboard shows one combined timeline.
- Dashboard storage stays local by default; delete the file (or click Clear) to wipe history. If you click Investigate with AI, the AI investigation may send captured SQL, model/source context, query plans, and bounded result rows to your configured provider.
- Prefer zero filesystem footprint? Set
'DASHBOARD_PERSIST': Falseto use a per-process in-memory ring buffer instead (history dies with the process).
Security
Section titled “Security”- DEBUG-only: every dashboard view returns 404 when
settings.DEBUGis off. The dashboard exposes raw SQL and code paths; keep theif settings.DEBUG:guard around the URL include as a second layer.