--- /dev/null
+"""
+Debug Toolbar middleware
+"""
+import re
+from django.conf import settings
+from django.utils.encoding import smart_str
+from debug_toolbar.toolbar.loader import DebugToolbar
+
+_HTML_TYPES = ('text/html', 'application/xhtml+xml')
+_END_HEAD_RE = re.compile(r'</head>', re.IGNORECASE)
+_END_BODY_RE = re.compile(r'<body[^<]*>', re.IGNORECASE)
+
+class DebugToolbarMiddleware(object):
+ """
+ Middleware to set up Debug Toolbar on incoming request and render toolbar
+ on outgoing response.
+ """
+ def __init__(self):
+ self.debug_toolbar = None
+
+ def show_toolbar(self, request):
+ if not settings.DEBUG:
+ return False
+ if not request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
+ return False
+ return True
+
+ def process_request(self, request):
+ if self.show_toolbar(request):
+ self.debug_toolbar = DebugToolbar(request)
+ self.debug_toolbar.load_panels()
+ debug = request.GET.get('djDebug')
+ # kinda ugly, needs changes to the loader to optimize
+ for panel in self.debug_toolbar.panels:
+ response = panel.process_request(request)
+ if not response:
+ if debug == panel.name:
+ response = panel.process_ajax(request)
+ if response:
+ response.skip_debug_response = True
+ return response
+
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ if self.show_toolbar(request):
+ for panel in self.debug_toolbar.panels:
+ cb = panel.process_view(request, callback, callback_args, callback_kwargs)
+ if cb:
+ callback = cb
+ return callback
+
+ def process_response(self, request, response):
+ if self.show_toolbar(request) and not getattr(response, 'skip_debug_response', False):
+ if response['Content-Type'].split(';')[0] in _HTML_TYPES and not request.is_ajax():
+ # Saving this here in case we ever need to inject into <head>
+ #response.content = _END_HEAD_RE.sub(smart_str(self.debug_toolbar.render_styles() + "%s" % match.group()), response.content)
+ for panel in self.debug_toolbar.panels:
+ nr = panel.process_response(request, response)
+ # Incase someone forgets `return response`
+ if nr: response = nr
+ response.content = _END_BODY_RE.sub(smart_str('<body>' + self.debug_toolbar.render_toolbar()), response.content)
+ return response
--- /dev/null
+"""Base DebugPanel class"""
+
+class DebugPanel(object):
+ """
+ Base class for debug panels.
+ """
+ # name = Base
+
+ def __init__(self, request):
+ self.request = request
+
+ def process_request(self, request):
+ return None
+
+ def process_response(self, request, response):
+ return response
+
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ return None
+
+ def dom_id(self):
+ return 'djDebug%sPanel' % (self.name.replace(' ', ''))
+
+ def title(self):
+ raise NotImplementedError
+
+ def url(self):
+ raise NotImplementedError
+
+ def content(self):
+ # TODO: This is a bit flaky in that panel.content() returns a string
+ # that gets inserted into the toolbar HTML template.
+ raise NotImplementedError
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+try: from cStringIO import StringIO
+except ImportError: import StringIO
+from django.core import cache
+from django.core.cache.backends.base import BaseCache
+import time
+import inspect
+import os.path
+from django.template.loader import render_to_string
+
+class CacheStatTracker(BaseCache):
+ """A small class used to track cache calls."""
+ def __init__(self, cache):
+ self.cache = cache
+ self.reset()
+
+ def reset(self):
+ self.calls = []
+ self.hits = 0
+ self.misses = 0
+ self.sets = 0
+ self.gets = 0
+ self.get_many = 0
+ self.deletes = 0
+ self.total_time = 0
+
+ def _get_func_info(self):
+ stack = inspect.stack()[2]
+ return (os.path.basename(stack[1]), stack[2], stack[3], stack[4])
+
+ def get(self, key, default=None):
+ t = time.time()
+ value = self.cache.get(key, default)
+ this_time = time.time()-t
+ self.total_time += this_time*1000
+ if value is None:
+ self.misses += 1
+ else:
+ self.hits += 1
+ self.gets += 1
+ self.calls.append((this_time, 'get', (key,), self._get_func_info()))
+ return value
+
+ def set(self, key, value, timeout=None):
+ t = time.time()
+ self.cache.set(key, value, timeout)
+ this_time = time.time()-t
+ self.total_time += this_time*1000
+ self.sets += 1
+ self.calls.append((this_time, 'set', (key, value, timeout), self._get_func_info()))
+
+ def delete(self, key):
+ t = time.time()
+ self.instance.delete(key, value)
+ this_time = time.time()-t
+ self.total_time += this_time*1000
+ self.deletes += 1
+ self.calls.append((this_time, 'delete', (key,), self._get_func_info()))
+
+ def get_many(self, keys):
+ t = time.time()
+ results = self.cache.get_many(keys)
+ this_time = time.time()-t
+ self.total_time += this_time*1000
+ self.get_many += 1
+ for key, value in results.iteritems():
+ if value is None:
+ self.misses += 1
+ else:
+ self.hits += 1
+ self.calls.append((this_time, 'get_many', (keys,), self._get_func_info()))
+
+class CacheDebugPanel(DebugPanel):
+ """
+ Panel that displays the cache statistics.
+ """
+ name = 'Cache'
+
+ def __init__(self, request):
+ # This is hackish but to prevent threading issues
+ # is somewhat needed
+ if isinstance(cache.cache, CacheStatTracker):
+ cache.cache.reset()
+ self.cache = cache.cache
+ else:
+ self.cache = CacheStatTracker(cache.cache)
+ cache.cache = self.cache
+ super(CacheDebugPanel, self).__init__(request)
+
+ def title(self):
+ return 'Cache: %.2fms' % self.cache.total_time
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = dict(
+ cache_calls = len(self.cache.calls),
+ cache_time = self.cache.total_time,
+ cache = self.cache,
+ )
+ return render_to_string('debug_toolbar/panels/cache.html', context)
\ No newline at end of file
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+from django.template.loader import render_to_string
+
+class HeaderDebugPanel(DebugPanel):
+ """
+ A panel to display HTTP headers.
+ """
+ name = 'Header'
+ # List of headers we want to display
+ header_filter = [
+ 'CONTENT_TYPE',
+ 'HTTP_ACCEPT',
+ 'HTTP_ACCEPT_CHARSET',
+ 'HTTP_ACCEPT_ENCODING',
+ 'HTTP_ACCEPT_LANGUAGE',
+ 'HTTP_CACHE_CONTROL',
+ 'HTTP_CONNECTION',
+ 'HTTP_HOST',
+ 'HTTP_KEEP_ALIVE',
+ 'HTTP_REFERER',
+ 'HTTP_USER_AGENT',
+ 'QUERY_STRING',
+ 'REMOTE_ADDR',
+ 'REMOTE_HOST',
+ 'REQUEST_METHOD',
+ 'SCRIPT_NAME',
+ 'SERVER_NAME',
+ 'SERVER_PORT',
+ 'SERVER_PROTOCOL',
+ 'SERVER_SOFTWARE',
+ ]
+ def title(self):
+ return 'HTTP Headers'
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = {
+ 'headers': dict([(k, self.request.META[k]) for k in self.header_filter if k in self.request.META]),
+ }
+ return render_to_string('debug_toolbar/panels/headers.html', context)
\ No newline at end of file
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+from django.template.loader import render_to_string
+
+class HttpVarsDebugPanel(DebugPanel):
+ """
+ A panel to display HTTP variables (POST/GET).
+ """
+ name = 'HttpVars'
+ # List of headers we want to display
+
+ def title(self):
+ return 'HTTP Globals'
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = {
+ 'get': [(k, self.request.GET.getlist(k)) for k in self.request.GET.iterkeys()],
+ 'post': [(k, self.request.POST.getlist(k)) for k in self.request.POST.iterkeys()],
+ 'session': [(k, self.request.session.get(k)) for k in self.request.session.iterkeys()],
+ 'cookies': [(k, self.request.COOKIES.get(k)) for k in self.request.COOKIES.iterkeys()],
+ }
+ return render_to_string('debug_toolbar/panels/http_vars.html', context)
\ No newline at end of file
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+try: import cProfile as profile
+except ImportError: import profile
+import pstats
+from django.template.loader import render_to_string
+
+class ProfilerDebugPanel(DebugPanel):
+ """
+ Panel that displays the time a response took with cProfile output.
+ """
+ name = 'Profiler'
+
+ def __init__(self, request):
+ super(ProfilerDebugPanel, self).__init__(request)
+
+ def process_response(self, request, response):
+ stats = pstats.Stats(self.profiler)
+ function_calls = []
+ for func in stats.strip_dirs().sort_stats(1).fcn_list:
+ current = []
+ if stats.stats[func][0] != stats.stats[func][1]:
+ current.append('%d/%d' % (stats.stats[func][1], stats.stats[func][0]))
+ else:
+ current.append(stats.stats[func][1])
+ current.append(stats.stats[func][2]*1000)
+ current.append(stats.stats[func][2]*1000/stats.stats[func][1])
+ current.append(stats.stats[func][3]*1000)
+ current.append(stats.stats[func][3]*1000/stats.stats[func][0])
+ current.append(pstats.func_std_string(func))
+ function_calls.append(current)
+ self.stats = stats
+ self.function_calls = function_calls
+ return response
+
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ self.callback = callback
+ self.profiler = profile.Profile()
+ return self.profiler.runcall(callback, request, *callback_args, **callback_kwargs)
+
+ def title(self):
+ return 'View: %.2fms' % (float(self.stats.total_tt)*1000,)
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = {
+ 'callback': self.callback.__name__,
+ 'module': self.callback.__module__,
+ 'stats': self.stats,
+ 'function_calls': self.function_calls,
+ }
+ return render_to_string('debug_toolbar/panels/profiler.html', context)
\ No newline at end of file
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+from django.db import connection
+from django.db.backends import util
+from django.template.loader import render_to_string
+from django.shortcuts import render_to_response
+from django.http import HttpResponse
+from django.utils import simplejson
+from time import time
+
+class DatabaseStatTracker(util.CursorDebugWrapper):
+ """Replacement for CursorDebugWrapper which stores additional information
+ in `connection.queries`."""
+ def execute(self, sql, params=()):
+ start = time()
+ try:
+ return self.cursor.execute(sql, params)
+ finally:
+ stop = time()
+ # We keep `sql` to maintain backwards compatibility
+ self.db.queries.append({
+ 'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
+ 'time': stop - start,
+ 'raw_sql': sql,
+ 'params': params,
+ })
+
+util.CursorDebugWrapper = DatabaseStatTracker
+
+class SQLDebugPanel(DebugPanel):
+ """
+ Panel that displays information about the SQL queries run while processing the request.
+ """
+ name = 'SQL'
+
+ def reformat_sql(self, sql):
+ sql = sql.replace('`,`', '`, `')
+ sql = sql.replace('` FROM `', '` \n FROM `')
+ sql = sql.replace('` WHERE ', '` \n WHERE ')
+ sql = sql.replace('`) WHERE ', '`) \n WHERE ')
+ sql = sql.replace(' ORDER BY ', ' \n ORDER BY ')
+ return sql
+
+ def process_ajax(self, request):
+ action = request.GET.get('op')
+ if action == 'explain':
+ # XXX: loop through each sql statement to output explain?
+ sql = request.GET.get('sql', '').split(';')[0]
+ if sql.lower().startswith('select'):
+ if 'params' in request.GET:
+ params = simplejson.loads(request.GET['params'])
+ else:
+ params = []
+ cursor = connection.cursor()
+ cursor.execute("EXPLAIN %s" % (sql,), params)
+ response = cursor.fetchall()
+ cursor.close()
+ context = {'explain': response, 'sql': self.reformat_sql(sql), 'params': params}
+ return render_to_response('debug_toolbar/panels/sql_explain.html', context)
+ else:
+ return HttpResponse('Invalid SQL', mimetype="text/plain", status_code=403)
+
+ def process_request(self, request):
+ self.queries_offset = len(connection.queries)
+
+ def process_response(self, request, response):
+ self.queries = connection.queries[self.queries_offset:]
+ self.total_time = sum(map(lambda q: float(q['time'])*1000, self.queries))
+ return response
+
+ def title(self):
+ return 'SQL: %.2fms (%d queries)' % (self.total_time, len(self.queries))
+
+ def url(self):
+ return ''
+
+ def content(self):
+ queries = [(q['time'], q['sql'], q['raw_sql'], simplejson.dumps(q['params'])) for q in sorted(self.queries, key=lambda x: x['time'])[::-1]]
+ context = {'queries': queries}
+ return render_to_string('debug_toolbar/panels/sql.html', context)
\ No newline at end of file
--- /dev/null
+from debug_toolbar.panels import DebugPanel
+from django.conf import settings
+from django.dispatch import dispatcher
+from django.core.signals import request_started
+from django.test.signals import template_rendered
+from django.template.loader import render_to_string
+
+# Based on http://www.djangosnippets.org/snippets/766/
+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='<Unknown Template>'):
+ old_template_init(self, template_string, origin, name)
+ self.origin = origin
+Template.__init__ = new_template_init
+
+class TemplatesDebugPanel(DebugPanel):
+ """
+ Panel that displays information about the SQL queries run while processing the request.
+ """
+ name = 'Templates'
+
+ def __init__(self, *args, **kwargs):
+ super(TemplatesDebugPanel, self).__init__(*args, **kwargs)
+ self.templates_used = []
+ self.contexts_used = []
+ template_rendered.connect(self._storeRenderedTemplates)
+
+ def _storeRenderedTemplates(self, **kwargs):
+ template = kwargs.pop('template')
+ if template:
+ self.templates_used.append(template)
+ context = kwargs.pop('context')
+ if context:
+ self.contexts_used.append(context)
+
+ def process_response(self, request, response):
+ self.templates = [
+ (t.name, t.origin and t.origin.name or 'No origin')
+ for t in self.templates_used
+ ]
+ return response
+
+ def title(self):
+ return 'Templates: %.2fms'
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = {
+ 'templates': self.templates,
+ 'template_dirs': settings.TEMPLATE_DIRS,
+ }
+
+ return render_to_string('debug_toolbar/panels/templates.html', context)
\ No newline at end of file
--- /dev/null
+import time
+from debug_toolbar.panels import DebugPanel
+
+class TimerDebugPanel(DebugPanel):
+ """
+ Panel that displays the time a response took.
+ """
+ name = 'Timer'
+
+ def __init__(self, request):
+ super(TimerDebugPanel, self).__init__(request)
+ self._start_time = time.time()
+
+ def title(self):
+ return 'Time: %0.2fms' % ((time.time() - self._start_time) * 1000)
+
+ def url(self):
+ return ''
+
+ def content(self):
+ return ''
--- /dev/null
+import django
+from debug_toolbar.panels import DebugPanel
+
+class VersionDebugPanel(DebugPanel):
+ """
+ Panel that displays the Django version.
+ """
+ name = 'Version'
+
+ def title(self):
+ return 'Version: %s' % (django.get_version())
+
+ def url(self):
+ return ''
+
+ def content(self):
+ return ''
--- /dev/null
+from django.conf import settings
+
+DEBUG_TOOLBAR_PANELS = getattr(settings, 'DEBUG_TOOLBAR_PANELS', (
+ 'debug_toolbar.panels.version.VersionDebugPanel',
+ 'debug_toolbar.panels.profiler.ProfilerDebugPanel',
+ 'debug_toolbar.panels.sql.SQLDebugPanel',
+ 'debug_toolbar.panels.cache.CacheDebugPanel',
+ # 'debug_toolbar.panels.templates.TemplatesDebugPanel',
+ 'debug_toolbar.panels.headers.HeaderDebugPanel',
+ 'debug_toolbar.panels.http_vars.HttpVarsDebugPanel',
+))
--- /dev/null
+<script type="text/javascript">
+var djDebugLastObj = null;
+function djDebugToggle(tag) {
+ if (djDebugLastObj) {
+ djDebugLastObj.className = '';
+ document.getElementById(djDebugLastObj.getAttribute('rel')).style.display = 'none';
+ }
+ djDebugWindowClose();
+ var obj = document.getElementById(tag.getAttribute('rel'));
+ if (!obj) return;
+ if (djDebugLastObj == tag) {
+ djDebugLastObj = null;
+ return;
+ }
+ if (obj.style.display != 'block') {
+ obj.style.display = 'block';
+ tag.className = 'active';
+ djDebugLastObj = tag;
+ }
+}
+function djDebugClose() {
+ if (!djDebugLastObj) return;
+ djDebugLastObj.className = '';
+ document.getElementById(djDebugLastObj.getAttribute('rel')).style.display = 'none';
+ djDebugLastObj = null;
+}
+function djDebugWindowHtml(text) {
+ var obj = document.getElementById('djDebugWindow');
+ var frame = obj.getElementsByTagName('div')[1];
+ frame.innerHTML = text;
+}
+function djDebugWindow(url) {
+ var obj = document.getElementById('djDebugWindow');
+ djDebugWindowHtml('Loading request...');
+ djDebugHttp.open('get', url, true);
+
+ djDebugHttp.onreadystatechange = function() {
+ if (djDebugHttp.readyState == 4) {
+ // XXX: why does status return 0?
+ if (djDebugHttp.status == 200 || djDebugHttp.status == 0) {
+ djDebugWindowHtml(djDebugHttp.responseText);
+ }
+ else {
+ djDebugWindowHtml('There was an error loading the document.');
+ }
+ }
+ }
+ djDebugHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ try {
+ djDebugHttp.send(null);
+ }
+ catch (ex) {
+ djDebugWindowHtml('There was an error loading the document.');
+ }
+ obj.style.display = 'block';
+}
+function djDebugCreateXMLHttpRequest() {
+ var http;
+ if (window.XMLHttpRequest) { // Mozilla, Safari,...
+ http = new XMLHttpRequest();
+ if (http.overrideMimeType) {
+ // set type accordingly to anticipated content type
+ http.overrideMimeType('text/html');
+ }
+ }
+ else if (window.ActiveXObject) { // IE
+ try {
+ http = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ http = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e) {}
+ }
+ }
+ if (!http) {
+ alert('Cannot create XMLHTTP instance');
+ return false;
+ }
+ return http;
+}
+var djDebugHttp = djDebugCreateXMLHttpRequest();
+function djDebugWindowClose() {
+ var obj = document.getElementById('djDebugWindow');
+ obj.style.display = 'none';
+}
+function djDebugCloseAll() {
+ djDebugClose();
+ djDebugWindowClose();
+}
+function djDebugHide() {
+ var obj = document.getElementById('djDebug');
+ obj.style.display = 'none';
+}
+</script>
+<style type="text/css">
+#djDebug * { font: normal 12px Arial, sans-serif; margin: 0; padding: 0; float: none; position: static; border: 0; }
+#djDebugToolbar {
+ height:22px;
+ background:#244035;
+ color:#90EF45;
+ text-transform:lowercase;
+ margin: 5px 0;
+ z-index:100000000;
+ border-top:1px solid #345447;
+ border-bottom:1px solid #10211a;
+}
+#djDebugToolbar ul { margin: 0; padding: 0 10px; }
+#djDebugToolbar li { display: inline; height: 22px; float: left; }
+#djDebugToolbar li span,
+#djDebugToolbar li a { color: #FFE761; text-decoration: none; display: inline; width: auto; position: relative; float: none; margin: 0; height: 12px; line-height: 22px; padding: 4px 10px; border-left: 1px solid #345447; border-right: 1px solid #10211a; padding-right: 10px; }
+#djDebugToolbar li a:hover, #djDebugToolbar li.active a { color: #fff; background-color: #2e4d41; }
+#djDebugToolbar #djDebugButton span { border-left: 0; color: #fff; font-weight: bold; }
+#djDebugToolbar #djDebugHide { float: right; }
+#djDebugToolbar #djDebugHide a { border: 0; font-size: 93%; }
+#djDebug .panelContent {
+ display:none;
+ position:absolute;
+ margin:0;
+ padding:10px;
+ top:43px;
+ width:auto;
+ left:10px;
+ right:10px;
+ bottom:10px;
+ background:#244035;
+ color:#fff;
+ z-index:1000000;
+ overflow:auto;
+ border:1px solid #345447;
+}
+#djDebug .panelContent * { color: #fff; }
+#djDebug .panelContent a:hover { color: #FFE761; }
+#djDebug .panelContent table { width: 100%; }
+#djDebug .panelContent p { padding: 5px; }
+#djDebug .panelContent p, #djDebug .panelContent table,
+#djDebug .panelContent ul, #djDebug .panelContent dl,
+#djDebug .panelContent .title { margin: 5px 0; }
+#djDebug .panelContent dt, #djDebug .panelContent dd { display: block; }
+#djDebug .panelContent dt { font-weight: bold; width: 100px; clear: left; float: left; }
+#djDebug .panelContent dd { margin-left: 20px; margin-bottom: 5px; margin-left: 100px; }
+#djDebug .panelContent th, #djDebug td { padding: 5px; }
+#djDebug .panelContent td { background:#244035; }
+#djDebug .panelContent th { font-weight: bold; text-align: left; background: transparent; color: #fff; }
+#djDebug .panelContent thead th { border-bottom: 1px solid #2e4d41; color: #FFE761; }
+#djDebug .panelContent .row2 td { background:#2e4d41; }
+#djDebugWindow { z-index: 20000000; }
+#djDebug .panelContent .title { font-weight: bold; font-size: 15px; color: #90EF45; }
+#djDebug .panelContent .title a { float: right; font-weight: bold; font-size: 10px; }
+#djDebug .panelContent .title .close { float: right; font-weight: bold; margin-left: 15px; }
+</style>
+
+<div id="djDebug">
+ <div id="djDebugToolbar">
+ <ul id="djDebugPanelList">
+ <li id="djDebugButton"><span>Django Debug</span></li>
+ {% for panel in panels %}
+ <li rel="{{ panel.dom_id }}">
+ {% if panel.content %}
+ <a href="{{ panel.url|default:"#" }}" onclick="djDebugToggle(this.parentNode)" title="{{ panel.title }}" class="{{ panel.dom_id }}">{{ panel.title }}</a>
+ {% else %}
+ <span>{{ panel.title }}</span>
+ {% endif %}
+ </li>
+ {% endfor %}
+ <li id="djDebugHide"><a href="javascript:djDebugHide();">Hide Debug Toolbar</a></li>
+ </ul>
+ </div>
+ {% for panel in panels %}
+ {% with panel.content as content %}
+ {% if content %}
+ <div id="{{ panel.dom_id }}" class="panelContent">
+ <div class="title">
+ <a href="javascript:djDebugCloseAll()" class="close">Close</a>
+ <!-- <h1>{{ panel.title }}</h1> -->
+ </div>
+ <div class="content">
+ {{ content|safe }}
+ </div>
+ </div>
+ {% endif %}
+ {% endwith %}
+ {% endfor %}
+ <div id="djDebugWindow" class="panelContent">
+ <div class="title">
+ <a href="javascript:djDebugCloseAll()" class="close">Close</a>
+ <a href="javascript:djDebugWindowClose();" class="close">Back</a>
+ </div>
+ <div class="content">
+ </div>
+ </div>
+</div>
--- /dev/null
+<div class="title">
+ Cache Usage
+</div>
+<table>
+ <colgroup>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ </colgroup>
+ <tr>
+ <th>Total Calls</th>
+ <td>{{ cache_calls }}</td>
+ <th>Total Time</th>
+ <td>{{ cache_time }}ms</td>
+ <th>Hits</th>
+ <td>{{ cache.hits }}</td>
+ <th>Misses</th>
+ <td>{{ cache.misses }}</td>
+ </tr>
+ <tr>
+ <th>gets</th>
+ <td>{{ cache.gets }}</td>
+ <th>sets</th>
+ <td>{{ cache.sets }}</td>
+ <th>deletes</th>
+ <td>{{ cache.deletes }}</td>
+ <th>get_many</th>
+ <td>{{ cache.get_many }}</td>
+ </tr>
+</table>
+{% if cache.calls %}
+<div class="title">
+ Breakdown
+</div>
+<table>
+ <thead>
+ <tr>
+ <th>Time (ms)</th>
+ <th>Type</th>
+ <th>Parameters</th>
+ <th>Function</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for query in cache.calls %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ query.0|floatformat:"4" }}</td>
+ <td>{{ query.1|escape }}</td>
+ <td>{{ query.2|escape }}</td>
+ <td>{{ query.3.0 }}:{{ query.3.1 }}({{ query.3.2|escape }}); {{ query.3.3.0|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% endif %}
\ No newline at end of file
--- /dev/null
+<div class="title">
+ Request Headers
+</div>
+<table>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in headers.iteritems %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ key|escape }}</td>
+ <td>{{ value|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
\ No newline at end of file
--- /dev/null
+<div class="title">request.GET</div>
+{% if get %}
+<table>
+ <colgroup>
+ <col style="width:20%"/>
+ <col/>
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in get %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ key|escape }}</td>
+ <td>{{ value|join:", "|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% else %}
+<p>No GET variables.</p>
+{% endif %}
+<div class="title">request.POST</div>
+{% if post %}
+<table>
+ <colgroup>
+ <col style="width:20%"/>
+ <col/>
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in post %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ key|escape }}</td>
+ <td>{{ value|join:", "|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% else %}
+<p>No POST variables.</p>
+{% endif %}
+<div class="title">request.session</div>
+{% if session %}
+<table>
+ <colgroup>
+ <col style="width:20%"/>
+ <col/>
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in session %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ key|escape }}</td>
+ <td>{{ value|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% else %}
+<p>No SESSION variables.</p>
+{% endif %}
+<div class="title">request.COOKIES</div>
+{% if cookies %}
+<table>
+ <colgroup>
+ <col style="width:20%"/>
+ <col/>
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in cookies %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ key|escape }}</td>
+ <td>{{ value|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% else %}
+<p>No COOKIES variables.</p>
+{% endif %}
\ No newline at end of file
--- /dev/null
+<div class="title">
+ Profile Output
+</div>
+<p>`profile` module output for the view `{{ module|escape }}.{{ callback|escape }}`</p>
+<table>
+ <thead>
+ <tr>
+ <th>Calls</th>
+ <th>Total Time (ms)</th>
+ <th>Per Call (ms)</th>
+ <th>Cumulative Time (ms)</th>
+ <th>Per Call (ms)</th>
+ <th>Function</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for row in function_calls %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ row.0 }}</td>
+ <td>{{ row.1|floatformat:"4" }}</td>
+ <td>{{ row.2|floatformat:"4" }}</td>
+ <td>{{ row.3|floatformat:"4" }}</td>
+ <td>{{ row.4|floatformat:"4" }}</td>
+ <td>{{ row.5|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
\ No newline at end of file
--- /dev/null
+<div class="title">
+ SQL Queries
+</div>
+<table>
+ <thead>
+ <tr>
+ <th>Time (ms)</th>
+ <th>Query</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for query in queries %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ query.0|floatformat:"4" }}</td>
+ <td><a href="javascript:djDebugWindow('?djDebug=SQL&op=explain&sql={{ query.2|urlencode }}&params={{ query.3|urlencode }}');">{{ query.1|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
\ No newline at end of file
--- /dev/null
+<div class="title">SQL Explain</div>
+<dl>
+ <dt>Query:</dt>
+ <dd style="white-space:pre-wrap;">{{ sql|escape|linebreaksbr }}</dd>
+ <dt>Parameters:</dt>
+ <dd>{{ params|join:", "|escape }}</dd>
+</dl>
+<table>
+ <thead>
+ <tr>
+ <th>ID</th>
+ <th>Select Type</th>
+ <th>Table</th>
+ <th>Type</th>
+ <th>Possible Keys</th>
+ <th>Key</th>
+ <th>Ken Length</th>
+ <th>Ref</th>
+ <th>Rows</th>
+ <th>Extra</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for row in explain %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ row.0|escape }}</td>
+ <td>{{ row.1|escape }}</td>
+ <td>{{ row.2|escape }}</td>
+ <td>{{ row.3|escape }}</td>
+ <td>{{ row.4|escape }}</td>
+ <td>{{ row.5|escape }}</td>
+ <td>{{ row.6|escape }}</td>
+ <td>{{ row.7|escape }}</td>
+ <td>{{ row.8|escape }}</td>
+ <td>{{ row.9|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
\ No newline at end of file
--- /dev/null
+<div class="title">
+ Templates
+</div>
+<table>
+ <thead>
+ <tr>
+ <th>Time (ms)</th>
+ <th>Template</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for template in templates %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ template }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
\ No newline at end of file
--- /dev/null
+"""
+The main DebugToolbar class that loads and renders the Toolbar.
+"""
+from django.template.loader import render_to_string
+from debug_toolbar.settings import DEBUG_TOOLBAR_PANELS
+
+class DebugToolbar(object):
+
+ def __init__(self, request):
+ self.request = request
+ self.panels = []
+ self.panel_list = []
+ self.content_list = []
+
+ def load_panels(self):
+ """
+ Populate debug panel lists from settings.DEBUG_TOOLBAR_PANELS.
+ """
+ from django.conf import settings
+ from django.core import exceptions
+
+ for panel_path in DEBUG_TOOLBAR_PANELS:
+ try:
+ dot = panel_path.rindex('.')
+ except ValueError:
+ raise exceptions.ImproperlyConfigured, '%s isn\'t a debug panel module' % panel_path
+ panel_module, panel_classname = panel_path[:dot], panel_path[dot+1:]
+ try:
+ mod = __import__(panel_module, {}, {}, [''])
+ except ImportError, e:
+ raise exceptions.ImproperlyConfigured, 'Error importing debug panel %s: "%s"' % (panel_module, e)
+ try:
+ panel_class = getattr(mod, panel_classname)
+ except AttributeError:
+ raise exceptions.ImproperlyConfigured, 'Toolbar Panel module "%s" does not define a "%s" class' % (panel_module, panel_classname)
+
+ try:
+ panel_instance = panel_class(self.request)
+ except:
+ raise
+ continue # Some problem loading panel
+
+ self.panels.append(panel_instance)
+
+ def render_toolbar(self):
+ """
+ Renders the overall Toolbar with panels inside.
+ """
+ return render_to_string('debug_toolbar/base.html', {'panels': self.panels})
'django.core.context_processors.request',
)
-MIDDLEWARE_CLASSES = [
+MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.doc.XViewMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
'pagination.middleware.PaginationMiddleware',
-]
+)
+
+DEBUG_TOOLBAR_PANELS = (
+ 'debug_toolbar.panels.version.VersionDebugPanel',
+ 'debug_toolbar.panels.sql.SQLDebugPanel',
+ 'debug_toolbar.panels.timer.TimerDebugPanel',
+ 'debug_toolbar.panels.headers.HeaderDebugPanel',
+)
-# If DEBUG is enabled add query log to bottom of every template
-if DEBUG:
- MIDDLEWARE_CLASSES.append('middleware.ProfileMiddleware')
+INTERNAL_IPS = ('127.0.0.1', )
ROOT_URLCONF = 'urls'
'pagination',
'chunks',
'compress',
+ 'debug_toolbar',
'catalogue',
)