Skip to content
Open
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
21 changes: 11 additions & 10 deletions debug_toolbar/panels/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
]


def _monkey_patch_method(cache, name):
def _monkey_patch_method(cache, name, alias):
original_method = getattr(cache, name)

@functools.wraps(original_method)
Expand All @@ -39,15 +39,15 @@ def wrapper(*args, **kwargs):
if panel is None:
return original_method(*args, **kwargs)
else:
return panel._record_call(cache, name, original_method, args, kwargs)
return panel._record_call(cache, alias, name, original_method, args, kwargs)

setattr(cache, name, wrapper)


def _monkey_patch_cache(cache):
def _monkey_patch_cache(cache, alias):
if not hasattr(cache, "_djdt_patched"):
for name in WRAPPED_CACHE_METHODS:
_monkey_patch_method(cache, name)
_monkey_patch_method(cache, name, alias)
cache._djdt_patched = True


Expand Down Expand Up @@ -95,7 +95,7 @@ def wrapper(self, alias):
cache = original_method(self, alias)
panel = cls.current_instance()
if panel is not None:
_monkey_patch_cache(cache)
_monkey_patch_cache(cache, alias)
cache._djdt_panel = panel
return cache

Expand Down Expand Up @@ -138,7 +138,7 @@ def _store_call_info(
}
)

def _record_call(self, cache, name, original_method, args, kwargs):
def _record_call(self, cache, alias, name, original_method, args, kwargs):
# Some cache backends implement certain cache methods in terms of other cache
# methods (e.g. get_or_set() in terms of get() and add()). In order to only
# record the calls made directly by the user code, set the cache's _djdt_panel
Expand All @@ -161,7 +161,7 @@ def _record_call(self, cache, name, original_method, args, kwargs):
kwargs=kwargs,
trace=get_stack_trace(skip=2),
template_info=get_template_info(),
backend=cache,
backend=alias,
)
return value

Expand Down Expand Up @@ -194,9 +194,10 @@ def enable_instrumentation(self):
# requests. The monkey patch of CacheHander.create_connection() installed in
# the .ready() method will ensure that any new cache connections that get opened
# during this request will also be monkey patched.
for cache in caches.all(initialized_only=True):
_monkey_patch_cache(cache)
cache._djdt_panel = self
for alias in caches:
if hasattr(caches._connections, alias):
Copy link
Member

@tim-schilling tim-schilling Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it's going to be hard for us to remember that this is effectively the old version of initialized_only=True. Do you have any ideas on making this more maintainable? [Specifically if Django changes its implementation upstream.]

The only idea I have is to use a cache._djdt_panel.record = functools.partial(cache._djdt_panel.record, alias=alias)

Though it makes me wonder if we need the panel on the cache at all. Seems like we could just store a _djdt_record function on the cache, where then the partial gets much cleaner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though it makes me wonder if we need the panel on the cache at all. Seems like we could just store a _djdt_record function on the cache, where then the partial gets much cleaner.

We do not need the panel on the cache in the current code, it's just used a monkeypatch marker.

The only idea I have is to use a cache._djdt_panel.record = functools.partial(cache._djdt_panel.record, alias=alias)

I thought about something similar, but it needed more changes to the panel inner workings so I went with the most straightforward implementation to validate interest first. I could try a similar approach, storing a _djdt_record function.

_monkey_patch_cache(caches[alias], alias)
caches[alias]._djdt_panel = self
# Mark this panel instance as the current one for the active thread/async task
# context. This will be used by the CacheHander.create_connection() monkey
# patch.
Expand Down
2 changes: 2 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ Pending
conflicts
* Added CSS for resetting the height of elements too to avoid problems with
global CSS of a website where the toolbar is used.
* Show the cache backend alias instead of the cache instance for each call in
the cache panel.

5.1.0 (2025-03-20)
------------------
Expand Down
4 changes: 4 additions & 0 deletions tests/panels/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,7 @@ def test_generate_server_timing(self):
}

self.assertEqual(self.panel.get_server_timing_stats(), expected_data)

def test_backend_alias_is_recorded(self):
cache.cache.get("foo")
self.assertEqual(self.panel.calls[0]["backend"], "default")
Loading