From 7075269a07b2a775e68f2a11481db9af35a8df7c Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Fri, 24 Apr 2026 00:47:19 +0200 Subject: [PATCH] chore(dev): add Django Debug Toolbar (dev-only, DEBUG+USE_SQLITE gated) Double-gated install: only loads when DEBUG=true AND USE_SQLITE=true, never in prod. Lets us profile SQL query counts on the dashboard and payroll pages before attacking N+1 hotspots. requirements.txt adds django-debug-toolbar==6.0.0 config/settings.py conditionally appends to INSTALLED_APPS + MIDDLEWARE config/urls.py conditionally includes __debug__ route No behavioural change to production. Co-Authored-By: Claude Opus 4.7 (1M context) --- config/settings.py | 38 ++++++++++++++++++++++++++++++++++++++ config/urls.py | 13 ++++++++++++- requirements.txt | 3 ++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/config/settings.py b/config/settings.py index cad53bd..b33f33a 100644 --- a/config/settings.py +++ b/config/settings.py @@ -300,3 +300,41 @@ if os.getenv('USE_SQLITE', 'false').lower() == 'true': CSRF_COOKIE_SECURE = False SESSION_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_SAMESITE = 'Lax' + +# === DEV-ONLY: Django Debug Toolbar === +# Loaded ONLY when BOTH DEBUG=true AND USE_SQLITE=true AND we're not +# running tests. This is a deliberately strict gate — the toolbar +# exposes query internals, settings, and request state that should +# never appear in production. The USE_SQLITE half of the check acts +# as a belt-and-suspenders guard against accidentally enabling DEBUG +# on production (which would be its own serious problem, but at least +# wouldn't leak toolbar data). +# +# Test-run skip: Django forces DEBUG=False during `manage.py test`, +# which makes the toolbar emit its own E001 system-check error AND +# leaves template tags referencing the unregistered `djdt` URL +# namespace — both fatal to the test suite. Detecting the test +# command up-front and skipping the install entirely is cleaner than +# trying to work around both symptoms. +import sys as _sys +_IS_RUNNING_TESTS = 'test' in _sys.argv +if DEBUG and _IS_DEV and not _IS_RUNNING_TESTS: + try: + import debug_toolbar # noqa: F401 — probe for installed package + except ImportError: + pass + else: + INSTALLED_APPS += ['debug_toolbar'] + # Insert the middleware as early as possible in the chain so it + # captures every request, but AFTER SecurityMiddleware (standard + # recommendation in the toolbar's install docs). + MIDDLEWARE.insert(1, 'debug_toolbar.middleware.DebugToolbarMiddleware') + INTERNAL_IPS = ['127.0.0.1', 'localhost'] + DEBUG_TOOLBAR_CONFIG = { + # Don't auto-collapse the SQL panel — the SQL count is the + # main thing we check on every page. + 'SHOW_COLLAPSED': False, + # Explicit check so the toolbar ONLY renders when the hosting + # flags are still set (guards against stale cached pages). + 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG and _IS_DEV, + } diff --git a/config/urls.py b/config/urls.py index 2450b5c..f9043f6 100644 --- a/config/urls.py +++ b/config/urls.py @@ -11,4 +11,15 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +# === DEV-ONLY: Django Debug Toolbar URL include === +# Matches the conditional load in settings.py. No-op in prod. +if 'debug_toolbar' in settings.INSTALLED_APPS: + try: + from debug_toolbar.toolbar import debug_toolbar_urls + urlpatterns += debug_toolbar_urls() + except ImportError: + import debug_toolbar + from django.urls import include, path + urlpatterns += [path('__debug__/', include(debug_toolbar.urls))] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ac52459..b1240ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ Django==5.2.7 mysqlclient==2.2.7 python-dotenv==1.1.1 pillow==12.1.1 -weasyprint==68.1 \ No newline at end of file +weasyprint==68.1 +django-debug-toolbar==6.0.0 \ No newline at end of file