Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions debug_toolbar/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,35 @@ def ready(self):
# allows panels like CachePanel to enable their instrumentation immediately.
for cls in DebugToolbar.get_panel_classes():
cls.ready()
_manage_migrations_visibility(self.name)

def import_models(self):
"""
Override import models to avoid allowing users to dynamically fetch a model
that doesn't have a table behind it unless they are using the DatabaseStore.
This also prevents the command `migrate --run-syncdb` from creating tables
for the models.
"""
dt_config = dt_settings.get_config()
if dt_config["TOOLBAR_STORE_CLASS"] == "debug_toolbar.store.DatabaseStore":
return super().import_models()
# Not using the database store, don't import the models
self.models = {}


def _manage_migrations_visibility(app_name):
"""
Adjust the toolbar's migration visibility by manipulating the
project's settings.

This is a hack since it's manipulating settings.
"""
if (
dt_settings.get_config()["TOOLBAR_STORE_CLASS"]
!= "debug_toolbar.store.DatabaseStore"
):
# This effectively hides the migrations by telling Django they don't exist.
settings.MIGRATION_MODULES.setdefault(app_name, None)


def check_template_config(config):
Expand Down
5 changes: 5 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ Pending
"Django Debug Toolbar was unable to parse value." when there's a decoding
error.
* Updated docs to show incompatibility with Django Channels.
* Hide the toolbar's migrations unless ``debug_toolbar.store.DatabaseStore``
is being used. This may change in the future.
* Hide ``debug_toolbar.HistoryEntry`` as a model unless
``debug_toolbar.store.DatabaseStore`` is being used. This may change in the
future.

6.0.0 (2025-07-22)
------------------
Expand Down
8 changes: 4 additions & 4 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,15 @@ Toolbar options
The DatabaseStore provides persistence and automatically cleans up old
entries based on the ``RESULTS_CACHE_SIZE`` setting.

Note: For full functionality, DatabaseStore requires migrations for
the debug_toolbar app:
Note: When using ``DatabaseStore`` migrations are required for
the ``debug_toolbar`` app:

.. code-block:: bash

python manage.py migrate debug_toolbar

For the DatabaseStore to work properly, you need to run migrations for the
debug_toolbar app. The migrations create the necessary database table to store
For the ``DatabaseStore`` to work properly, you need to run migrations for the
``debug_toolbar`` app. The migrations create the necessary database table to store
toolbar data.

.. _TOOLBAR_LANGUAGE:
Expand Down
4 changes: 4 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@

DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

# Force the MIGRATION_MODULES to always find our migrations.
# See debug_toolbar/apps.py::_manage_migrations_visibility
MIGRATION_MODULES = {"debug_toolbar": "debug_toolbar.migrations"}

# Debug Toolbar configuration

DEBUG_TOOLBAR_CONFIG = {
Expand Down
47 changes: 47 additions & 0 deletions tests/test_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from unittest.mock import patch

from django.apps import apps
from django.test import SimpleTestCase, override_settings

from debug_toolbar.apps import _manage_migrations_visibility


class AppsTestCase(SimpleTestCase):
@override_settings(
DEBUG_TOOLBAR_CONFIG={
"TOOLBAR_STORE_CLASS": "debug_toolbar.store.DatabaseStore"
}
)
@patch("debug_toolbar.apps.settings.MIGRATION_MODULES")
def test_migrations_are_visible(self, mocked_migration_modules):
_manage_migrations_visibility("debug_toolbar")
self.assertFalse(mocked_migration_modules.setdefault.called)

@override_settings(
DEBUG_TOOLBAR_CONFIG={"TOOLBAR_STORE_CLASS": "debug_toolbar.store.MemoryStore"}
)
@patch("debug_toolbar.apps.settings.MIGRATION_MODULES")
def test_migrations_are_hidden(self, mocked_migration_modules):
_manage_migrations_visibility("debug_toolbar")
mocked_migration_modules.setdefault.assert_called_once_with(
"debug_toolbar", None
)

@override_settings(
DEBUG_TOOLBAR_CONFIG={
"TOOLBAR_STORE_CLASS": "debug_toolbar.store.DatabaseStore"
}
)
def test_models_are_visible(self):
app_config = apps.get_app_config("debug_toolbar")
app_config.import_models()
apps.get_model("debug_toolbar", "HistoryEntry")

@override_settings(
DEBUG_TOOLBAR_CONFIG={"TOOLBAR_STORE_CLASS": "debug_toolbar.store.MemoryStore"}
)
def test_models_are_hidden(self):
app_config = apps.get_app_config("debug_toolbar")
app_config.import_models()
with self.assertRaises(LookupError):
apps.get_model("debug_toolbar", "HistoryEntry")
3 changes: 3 additions & 0 deletions tests/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ def test_get_store_with_setting(self):
self.assertIs(store.get_store(), StubStore)


@override_settings(
DEBUG_TOOLBAR_CONFIG={"TOOLBAR_STORE_CLASS": "debug_toolbar.store.DatabaseStore"}
)
class DatabaseStoreTestCase(TestCase):
@classmethod
def setUpTestData(cls) -> None:
Expand Down