From e6dfacc1f10d434930b6f3e1aa3426046406b0dc Mon Sep 17 00:00:00 2001 From: tschilling Date: Sat, 6 Jun 2015 10:50:03 -0400 Subject: [PATCH] Update the cache panel to support the cache_page decorator. Unfortunately, once the CacheMiddleware's caches/get_cache has been monkey patched, it can't be disabled. This results in a bit of overhead. --- debug_toolbar/panels/cache.py | 31 ++++++++++++++++++++++++------- docs/panels.rst | 2 +- tests/test_integration.py | 8 ++++++++ tests/urls.py | 1 + tests/views.py | 6 ++++++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index e8111a024..6e799dee0 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -11,6 +11,7 @@ get_cache as original_get_cache) from django.core.cache.backends.base import BaseCache from django.dispatch import Signal +from django.middleware import cache as middleware_cache from django.utils.translation import ugettext_lazy as _, ungettext @@ -124,17 +125,28 @@ def get_cache(*args, **kwargs): return CacheStatTracker(original_get_cache(*args, **kwargs)) -def get_cache_handler(): - if CacheHandler is None: - return None - +if CacheHandler is not None: class CacheHandlerPatch(CacheHandler): def __getitem__(self, alias): actual_cache = super(CacheHandlerPatch, self).__getitem__(alias) return CacheStatTracker(actual_cache) + + +def get_cache_handler(): + if CacheHandler is None: + return None return CacheHandlerPatch() +# Must monkey patch the middleware's cache module as well in order to +# cover per-view level caching. This needs to be monkey patched outside +# of the enable_instrumentation method since the django's +# decorator_from_middleware_with_args will store the cache from core.caches +# when it wraps the view. +middleware_cache.get_cache = get_cache +middleware_cache.caches = get_cache_handler() + + class CachePanel(Panel): """ Panel that displays the cache statistics. @@ -212,19 +224,24 @@ def title(self): count) % dict(count=count) def enable_instrumentation(self): - # This isn't thread-safe because cache connections aren't thread-local - # in Django, unlike database connections. cache.get_cache = get_cache if CacheHandler is None: cache.cache = CacheStatTracker(original_cache) else: - cache.caches = get_cache_handler() + if isinstance(middleware_cache.caches, CacheHandlerPatch): + cache.caches = middleware_cache.caches + else: + cache.caches = get_cache_handler() def disable_instrumentation(self): if CacheHandler is None: cache.cache = original_cache else: cache.caches = original_caches + # While it can be restored to the original, any views that were + # wrapped with the cache_page decorator will continue to use a + # monkey patched cache. + middleware_cache.caches = original_caches cache.get_cache = original_get_cache def process_response(self, request, response): diff --git a/docs/panels.rst b/docs/panels.rst index 6101d9b8b..7707a9a65 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -75,7 +75,7 @@ Cache Path: ``debug_toolbar.panels.cache.CachePanel`` -Cache queries. +Cache queries. Is incompatible with Django's per-site caching. Signal ~~~~~~ diff --git a/tests/test_integration.py b/tests/test_integration.py index 3c79adedb..009a09f1e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -86,6 +86,14 @@ def test_middleware_response_insertion(self): # check toolbar insertion before "" self.assertContains(resp, '\n') + def test_cache_page(self): + self.client.get('/cached_view/') + self.assertEqual( + len(self.toolbar.get_panel_by_id('CachePanel').calls), 3) + self.client.get('/cached_view/') + self.assertEqual( + len(self.toolbar.get_panel_by_id('CachePanel').calls), 5) + @override_settings(DEBUG=True) class DebugToolbarIntegrationTestCase(TestCase): diff --git a/tests/urls.py b/tests/urls.py index 0a7ac19b3..e18ff3683 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -20,5 +20,6 @@ url(r'^non_ascii_request/$', 'regular_view', {'title': NonAsciiRepr()}), url(r'^new_user/$', 'new_user'), url(r'^execute_sql/$', 'execute_sql'), + url(r'^cached_view/$', 'cached_view'), url(r'^__debug__/', include(debug_toolbar.urls)), ) diff --git a/tests/views.py b/tests/views.py index 73e008786..186e745bb 100644 --- a/tests/views.py +++ b/tests/views.py @@ -5,6 +5,7 @@ from django.contrib.auth.models import User from django.http import HttpResponse from django.shortcuts import render +from django.views.decorators.cache import cache_page def execute_sql(request): @@ -24,3 +25,8 @@ def new_user(request, username='joe'): def resolving_view(request, arg1, arg2): # see test_url_resolving in tests.py return HttpResponse() + + +@cache_page(60) +def cached_view(request): + return HttpResponse()