In a Django project, how do you install django-debug-toolbar, and why might it still not appear on your pages?
answer
- app, URLs, middleware, IPs
- DEBUG and INTERNAL_IPS together
- injected before </body>
- encoded or JSON responses skipped
basics
~20 sAdd debug_toolbar to INSTALLED_APPS, its URLs via debug_toolbar_urls(), DebugToolbarMiddleware early in MIDDLEWARE, and 127.0.0.1 to INTERNAL_IPS. It stays hidden if DEBUG is False, your IP is not internal, or the response is not HTML with a </body>.
solid answer
~40 sThe setup has five steps: `pip install django-debug-toolbar`, add `"debug_toolbar"` to `INSTALLED_APPS` (it needs `django.contrib.staticfiles` and a `DjangoTemplates` backend with `APP_DIRS`), append `debug_toolbar_urls()` from `debug_toolbar.toolbar` to `urlpatterns`, put `debug_toolbar.middleware.DebugToolbarMiddleware` as early in `MIDDLEWARE` as possible but after any encoding middleware such as `GZipMiddleware`, and set `INTERNAL_IPS = ["127.0.0.1"]`. The default `SHOW_TOOLBAR_CALLBACK` shows it only when `DEBUG` is `True` and `REMOTE_ADDR` is in `INTERNAL_IPS`. When it does not appear, check those two first (in Docker, `REMOTE_ADDR` is the gateway, not 127.0.0.1), then the response: the toolbar is inserted before `</body>` in uncompressed, non-streaming HTML only, so JSON endpoints and templates without a body tag never show it.
code
python · 20 lines# settings.py (development)
INSTALLED_APPS = [
# ...
"django.contrib.staticfiles",
"debug_toolbar",
]
MIDDLEWARE = [
"django.middleware.gzip.GZipMiddleware", # encoding middleware stays before the toolbar
"debug_toolbar.middleware.DebugToolbarMiddleware",
"django.middleware.security.SecurityMiddleware",
# ...
]
INTERNAL_IPS = ["127.0.0.1"]
# urls.py
from debug_toolbar.toolbar import debug_toolbar_urls
urlpatterns = [
# ... the project's own routes ...
] + debug_toolbar_urls()go deeper
Recall the five wiring steps and the two conditions of the default callback: DEBUG on and your address in INTERNAL_IPS.
Explain how the middleware instruments a request and injects HTML, and why compressed, streaming, JSON or body-less responses are skipped.
Troubleshoot environments such as containers and proxies, keep the toolbar out of test runs, and make sure its guards cannot be widened by accident.
Standardise local tooling: a development settings module every engineer uses, so profiling tools are one flag away and never part of the production image.
## What the toolbar is **django-debug-toolbar** is a third-party Django app that adds a collapsible panel to every HTML page in development. For each request it shows the SQL that ran, the templates rendered, cache calls, signals, headers, settings and timing. It is the first tool most Django developers reach for when a page is slow or does something unexpected. ## Installing it 1. **Install the package**: `python -m pip install django-debug-toolbar`, ideally in a development-only dependency group. 2. **Check prerequisites**: `django.contrib.staticfiles` in `INSTALLED_APPS` with `STATIC_URL` set, and a `TEMPLATES` entry using `django.template.backends.django.DjangoTemplates` with `APP_DIRS: True`. A default `startproject` already has both. 3. **Add the app**: `"debug_toolbar"` in `INSTALLED_APPS`. 4. **Add the URLs**: `urlpatterns += debug_toolbar_urls()` from `debug_toolbar.toolbar`. It mounts the toolbar's own views under the `__debug__/` prefix, and it returns an **empty list when `DEBUG` is `False`**. 5. **Add the middleware**: `"debug_toolbar.middleware.DebugToolbarMiddleware"` as early as possible in `MIDDLEWARE`, but **after** any middleware that encodes the response body, such as `django.middleware.gzip.GZipMiddleware`. 6. **Set `INTERNAL_IPS`**: Django's default is an empty list, so add `"127.0.0.1"`. The middleware does the real work: for a request it decides to instrument, it enables each panel's instrumentation, lets the request run, collects statistics, and inserts the toolbar's HTML into the response. ## When the toolbar decides to show The decision is a callable named by the `SHOW_TOOLBAR_CALLBACK` option in `DEBUG_TOOLBAR_CONFIG`. The default, `debug_toolbar.middleware.show_toolbar`, returns `True` only when: - `settings.DEBUG` is `True`, **and** - `request.META["REMOTE_ADDR"]` is in `settings.INTERNAL_IPS`. Then the response must be one the toolbar can modify: - its content type is `text/html` or `application/xhtml+xml`; - it has no `Content-Encoding` (it is not already compressed); - it is not a streaming response; - it contains the `INSERT_BEFORE` marker, `</body>` by default. ## Why it does not appear: a checklist | Symptom | Likely cause | Fix | |---|---|---| | Nothing on any page | `DEBUG` is `False` | run the development settings | | Nothing on any page, `DEBUG` on | your `REMOTE_ADDR` is not in `INTERNAL_IPS` | add it; in Docker, use `show_toolbar_with_docker` as the callback | | Missing on some pages | the template has no `</body>` tag | use a real HTML layout | | Missing on API endpoints | the response is JSON | use the History panel from an HTML page, or a request recorder such as django-silk | | Missing everywhere with gzip enabled | the toolbar middleware sits before `GZipMiddleware` in the list and sees compressed bodies | move it after | | Toolbar HTML but no styling or script | static files misconfigured or a wrong MIME type for `.js` | fix static files; the toolbar docs show a `mimetypes` workaround | The Docker helper guesses the host's gateway address; its documentation warns it should only be used inside a container, because outside one the guess can match another machine on your network. ## Tests The toolbar should not be active while tests run. Django's test runner sets `DEBUG` to `False`, and the toolbar ships a system check, `debug_toolbar.E001`, that fires when it detects a setup likely to break under tests. The documented pattern is a `TESTING` flag (true when `"test"` is in `sys.argv` or pytest is running) that skips adding the app, the middleware and the URLs. ## Configuring it further The toolbar reads two optional settings: - **`DEBUG_TOOLBAR_PANELS`**: the list of panel classes, in display order; leave it unset to get the defaults (History, Versions, Timer, Settings, Headers, Request, SQL, Static files, Templates, Alerts, Cache, Signals, Tasks and the rest); - **`DEBUG_TOOLBAR_CONFIG`**: a dict of options merged over the defaults, such as `SHOW_TOOLBAR_CALLBACK`, `SHOW_COLLAPSED`, `DISABLE_PANELS`, `SQL_WARNING_THRESHOLD` and `RESULTS_CACHE_SIZE` (25 recent requests kept by the default in-memory store). The toolbar supports async views and ASGI, but its docs warn it still cannot handle concurrent requests, so keep the development server's defaults when using it.
- Why does django-debug-toolbar not show when the Django app runs in a Docker container on your laptop?Inside the container, requests arrive from the Docker network's gateway, so `REMOTE_ADDR` is something like `172.17.0.1`, not `127.0.0.1`, and the default callback rejects it. Either add that address to `INTERNAL_IPS` or set `SHOW_TOOLBAR_CALLBACK` to `debug_toolbar.middleware.show_toolbar_with_docker`, which tries to work out the gateway. Use that helper only inside a container.
- How can you inspect the SQL of a fetch request with django-debug-toolbar when the response is JSON?Load any HTML page with the toolbar, make the fetch request, then open the History panel: it lists recent requests and lets you switch the toolbar to that request's snapshot, including its SQL panel. The History panel is disabled when the server runs several processes or when `RENDER_PANELS` is `True`.
saying these in an interview costs you the question
- Adding debug_toolbar to INSTALLED_APPS is enough to see it
- The toolbar appears on JSON API responses too
- INTERNAL_IPS is about which hosts Django will serve
- The toolbar middleware should be the last entry in MIDDLEWARE
- The toolbar shows for staff users regardless of IP address