aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Cramer2011-09-26 17:28:17 -0700
committerDavid Cramer2011-09-26 17:28:17 -0700
commit49949387c863ecbbd043067c120b454d9377492a (patch)
treedeaeb4b454cda6c8d2128ce5aefcf59b2ec1d73b
parent8278bb0f24d925288740c23a2315e9c5a27a3290 (diff)
parent65394257a31226a4c2a34aa8a358f0651c5f90f8 (diff)
downloaddjango-debug-toolbar-49949387c863ecbbd043067c120b454d9377492a.tar.bz2
Merge branch 'issue-duplicated-queries' of https://github.com/thinred/django-debug-toolbar into thinred-issue-duplicated-queries
-rw-r--r--debug_toolbar/panels/template.py16
-rw-r--r--debug_toolbar/utils/tracking/db.py37
-rw-r--r--tests/tests.py16
3 files changed, 66 insertions, 3 deletions
diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py
index 79a4871..5c20fcd 100644
--- a/debug_toolbar/panels/template.py
+++ b/debug_toolbar/panels/template.py
@@ -6,7 +6,9 @@ from django.conf import settings
from django.template.context import get_standard_processors
from django.test.signals import template_rendered
from django.utils.translation import ugettext_lazy as _
+from django.db.models.query import QuerySet
from debug_toolbar.panels import DebugPanel
+from debug_toolbar.utils.tracking.db import recording, SQLQueryTriggered
# Code taken and adapted from Simon Willison and Django Snippets:
# http://www.djangosnippets.org/snippets/766/
@@ -68,8 +70,20 @@ class TemplateDebugPanel(DebugPanel):
# Replace LANGUAGES, which is available in i18n context processor
elif key == 'LANGUAGES' and isinstance(value, tuple):
temp_layer[key] = '<<languages>>'
+ # QuerySet would trigger the database: user can run the query from SQL Panel
+ elif isinstance(value, QuerySet):
+ model_name = "%s.%s" % (value.model._meta.app_label, value.model.__name__)
+ temp_layer[key] = '<<queryset of %s>>' % model_name
else:
- temp_layer[key] = value
+ try:
+ recording(False)
+ pformat(value) # this MAY trigger a db query
+ except SQLQueryTriggered:
+ temp_layer[key] = '<<triggers database query>>'
+ else:
+ temp_layer[key] = value
+ finally:
+ recording(True)
try:
context_list.append(pformat(temp_layer))
except UnicodeEncodeError:
diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py
index 7ffacef..1fa7b36 100644
--- a/debug_toolbar/utils/tracking/db.py
+++ b/debug_toolbar/utils/tracking/db.py
@@ -2,6 +2,7 @@ import inspect
import sys
from datetime import datetime
+from threading import local
from django.conf import settings
from django.template import Node
@@ -16,7 +17,39 @@ from debug_toolbar.utils.compat.db import connections
SQL_WARNING_THRESHOLD = getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}) \
.get('SQL_WARNING_THRESHOLD', 500)
-class CursorWrapper(object):
+class SQLQueryTriggered(Exception):
+ """Thrown when template panel triggers a query"""
+ pass
+
+class ThreadLocalState(local):
+ def __init__(self):
+ self.enabled = True
+
+ @property
+ def Wrapper(self):
+ return NormalCursorWrapper if self.enabled else ExceptionCursorWrapper
+
+ def recording(self, v):
+ self.enabled = v
+
+state = ThreadLocalState()
+recording = state.recording # export function
+
+def CursorWrapper(*args, **kwds): # behave like a class
+ return state.Wrapper(*args, **kwds)
+
+class ExceptionCursorWrapper(object):
+ """
+ Wraps a cursor and raises an exception on any operation.
+ Used in Templates panel.
+ """
+ def __init__(self, cursor, db, logger):
+ pass
+
+ def __getattr__(self, attr):
+ raise SQLQueryTriggered()
+
+class NormalCursorWrapper(object):
"""
Wraps a cursor and logs queries.
"""
@@ -103,4 +136,4 @@ class CursorWrapper(object):
return getattr(self.cursor, attr)
def __iter__(self):
- return iter(self.cursor) \ No newline at end of file
+ return iter(self.cursor)
diff --git a/tests/tests.py b/tests/tests.py
index b03e167..154615a 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -1,6 +1,7 @@
from debug_toolbar.middleware import DebugToolbarMiddleware
from debug_toolbar.panels.sql import SQLDebugPanel
from debug_toolbar.panels.request_vars import RequestVarsDebugPanel
+from debug_toolbar.panels.template import TemplateDebugPanel
from debug_toolbar.toolbar.loader import DebugToolbar
from debug_toolbar.utils import get_name_from_obj
from debug_toolbar.utils.tracking import pre_dispatch, post_dispatch, callbacks
@@ -9,6 +10,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.test import TestCase
+from django.template import Template, Context
from dingus import Dingus
import thread
@@ -202,6 +204,20 @@ class SQLPanelTestCase(BaseTestCase):
self.assertTrue('duration' in query[1])
self.assertTrue('stacktrace' in query[1])
+class TemplatePanelTestCase(BaseTestCase):
+ def test_queryset_hook(self):
+ template_panel = self.toolbar.get_panel(TemplateDebugPanel)
+ sql_panel = self.toolbar.get_panel(SQLDebugPanel)
+ t = Template("No context variables here!")
+ c = Context({ 'queryset' : User.objects.all(), 'deep_queryset' : { 'queryset' : User.objects.all() } })
+ t.render(c)
+ # ensure the query was NOT logged
+ self.assertEquals(len(sql_panel._queries), 0)
+ ctx = template_panel.templates[0]['context'][0]
+ ctx = eval(ctx) # convert back to Python
+ self.assertEquals(ctx['queryset'], '<<queryset of auth.User>>')
+ self.assertEquals(ctx['deep_queryset'], '<<triggers database query>>')
+
def module_func(*args, **kwargs):
"""Used by dispatch tests"""
return 'blah'