# Originally based on: http://djangosnippets.org/snippets/1872/ # Requires sqlparse: http://pypi.python.org/pypi/sqlparse import time from django.test.signals import template_rendered from django.conf import settings from django.db import connection from django.utils.encoding import force_unicode from django.utils.safestring import mark_safe TEMPLATE = """
+
""" # Monkeypatch instrumented test renderer from django.test.utils - we could use # django.test.utils.setup_test_environment for this but that would also set up # e-mail interception, which we don't want from django.test.utils import instrumented_test_render from django.template import Template, Context if Template.render != instrumented_test_render: Template.original_render = Template.render Template.render = instrumented_test_render # MONSTER monkey-patch old_template_init = Template.__init__ def new_template_init(self, template_string, origin=None, name=''): old_template_init(self, template_string, origin, name) self.origin = origin Template.__init__ = new_template_init class DebugFooter: def process_request(self, request): self.time_started = time.time() self.templates_used = [] self.contexts_used = [] self.sql_offset_start = len(connection.queries) template_rendered.connect(self._storeRenderedTemplates) def process_response(self, request, response): # Don't bother if the url doesn't have the "debug" query string # Added by Jeff Schroeder for dynamically enabling/disabling this if not request.GET.has_key("debug"): return response # Only include debug info for text/html pages not accessed via Ajax if 'text/html' not in response['Content-Type']: return response if request.is_ajax(): return response if response.status_code != 200: return response templates = [] for t in self.templates_used: if t.origin and t.origin.name: templates.append( (t.name, t.origin.name) ) else: templates.append( (t.name, "no origin") ) sql_queries = connection.queries[self.sql_offset_start:] # Reformat sql queries a bit sql_total = 0.0 sql_counts = {} for query in sql_queries: raw_sql = query['sql'] query['sql'] = reformat_sql(query['sql']) sql_total += float(query['time']) count = sql_counts.get(raw_sql,0) + 1 sql_counts[raw_sql] = count if count > 1: query['count'] = mark_safe('

duplicate query count=%s

' % count) else: query['count'] = '' from django.core.urlresolvers import resolve view_func = resolve(request.META['PATH_INFO'])[0] view = '%s.%s' % (view_func.__module__, view_func.__name__) vf = view_func breaker = 10 while not hasattr(vf,'func_code'): if hasattr(vf,'view_func'): vf = vf.view_func else: break # somethings wrong about the assumptions of the decorator breaker = breaker - 1 if breaker < 0: break if hasattr(vf,'func_code'): co = vf.func_code filename = co.co_filename lineno = co.co_firstlineno view = '- '.join([view, ':'.join([co.co_filename, str(co.co_firstlineno)])]) debug_content = Template(TEMPLATE).render(Context({ 'debug': settings.DEBUG, 'server_time': time.time() - self.time_started, 'templates': templates, 'sql': sql_queries, 'sql_total': sql_total, 'num_queries' : len(sql_queries), 'template_dirs': settings.TEMPLATE_DIRS, 'view': view })) content = response.content response.content = force_unicode(content).replace('', debug_content) return response def _storeRenderedTemplates(self, **kwargs): template = kwargs.get('template') if(template): self.templates_used.append(template) context = kwargs.get('context') if(context): self.contexts_used.append(context) def reformat_sql(sql): if sql: import sqlparse sql = sqlparse.format(sql, reindent=True, keyword_case='upper') return sql