From 98ce4d3f14c103d4a670d0cb66179f9b312cfe65 Mon Sep 17 00:00:00 2001 From: Harsh Singhaniya Date: Thu, 16 Jul 2026 01:24:20 +0530 Subject: [PATCH 1/3] Enhance cache panel to accurately track cache hits and misses for get() method --- debug_toolbar/panels/cache.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 24c942de7..2b94544c7 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -65,6 +65,7 @@ class CachePanel(Panel): is_async = True _context_locals = Local() + _missing_key = object() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -116,7 +117,12 @@ def _store_call_info( template_info, backend, ): - if name == "get" or name == "get_or_set": + if name == "get": + if return_value is self._missing_key: + self.misses += 1 + else: + self.hits += 1 + elif name == "get_or_set": if return_value is None: self.misses += 1 else: @@ -149,9 +155,22 @@ def _record_call(self, cache, alias, name, original_method, args, kwargs): # monkey-patched cache methods to skip recording additional calls made during # the course of this call, and then reset it back afterward. cache._djdt_panel = None + user_default = None try: start_time = perf_counter() - value = original_method(*args, **kwargs) + if name == "get": + user_default = kwargs.get("default", args[1] if len(args) > 1 else None) + # Replace the caller's default with an internal sentinel so a cache miss + # can be distinguished from a cached value equal to the supplied default. + if "default" in kwargs: + call_args = args + call_kwargs = {**kwargs, "default": self._missing_key} + else: + call_args = (args[0], self._missing_key, *args[2:]) + call_kwargs = kwargs + value = original_method(*call_args, **call_kwargs) + else: + value = original_method(*args, **kwargs) t = perf_counter() - start_time finally: cache._djdt_panel = self @@ -166,6 +185,10 @@ def _record_call(self, cache, alias, name, original_method, args, kwargs): template_info=get_template_info(), backend=f"{alias} ({type(cache).__name__})", ) + # Preserve the original cache.get() behavior by returning the caller's + # default instead of the internal sentinel on a cache miss. + if name == "get" and value is self._missing_key: + return user_default return value # Implement the Panel API From bde459c49a6b56c2f0917654d482aee6c5f30ef2 Mon Sep 17 00:00:00 2001 From: Harsh Singhaniya Date: Thu, 16 Jul 2026 01:24:33 +0530 Subject: [PATCH 2/3] Add tests for cache hits and misses with None and default values --- tests/panels/test_cache.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/panels/test_cache.py b/tests/panels/test_cache.py index fff28280b..e29cf0baa 100644 --- a/tests/panels/test_cache.py +++ b/tests/panels/test_cache.py @@ -45,6 +45,37 @@ def test_hits_and_misses(self): self.assertEqual(self.panel.hits, 4) self.assertEqual(self.panel.misses, 2) + def test_cached_none_counts_as_hit(self): + cache.cache.clear() + + cache.cache.set("foo", None) + self.assertIsNone(cache.cache.get("foo")) + self.assertEqual(self.panel.hits, 1) + self.assertEqual(self.panel.misses, 0) + + def test_missing_key_with_default_counts_as_miss(self): + cache.cache.clear() + + self.assertIsNone(cache.cache.get("foo", None)) + self.assertEqual(self.panel.hits, 0) + self.assertEqual(self.panel.misses, 1) + + def test_missing_key_returns_supplied_default(self): + cache.cache.clear() + + self.assertEqual(cache.cache.get("foo", "bar"), "bar") + self.assertEqual(self.panel.hits, 0) + self.assertEqual(self.panel.misses, 1) + + def test_cached_value_equal_to_default_counts_as_hit(self): + cache.cache.clear() + + cache.cache.set("foo", "bar") + self.assertEqual(cache.cache.get("foo", "bar"), "bar") + + self.assertEqual(self.panel.hits, 1) + self.assertEqual(self.panel.misses, 0) + def test_get_or_set_value(self): cache.cache.get_or_set("baz", "val") self.assertEqual(cache.cache.get("baz"), "val") From 7af7a621451045439a4d6f9bdcb706aca5f8512c Mon Sep 17 00:00:00 2001 From: Harsh Singhaniya Date: Thu, 16 Jul 2026 01:24:47 +0530 Subject: [PATCH 3/3] Update pending section in changes.rst --- docs/changes.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changes.rst b/docs/changes.rst index b21cc39fc..a3937d7bb 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -7,6 +7,10 @@ Pending * Fixed the Django version check in the SQL panel test suite for Django's boolean parameter handling. +* Fixed incorrect cache hit/miss reporting in the Cache panel for + ``cache.get()`` when a default value is supplied or the cached value is + ``None``. + 7.0.0 (2026-06-17) ------------------