Skip to content

Commit

Permalink
Show context where SQL query originated from template
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Lamb <lamby@debian.org>
Signed-off-by: Rob Hudson <rob@cogit8.org>
  • Loading branch information
lamby authored and robhudson committed Nov 2, 2009
1 parent a6804ad commit f6ada2c
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
54 changes: 54 additions & 0 deletions debug_toolbar/panels/sql.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from datetime import datetime
import os
import sys
import SocketServer
import traceback

import django
from django.conf import settings
from django.db import connection
from django.db.backends import util
from django.views.debug import linebreak_iter
from django.template import Node
from django.template.loader import render_to_string
from django.utils import simplejson
from django.utils.encoding import force_unicode
Expand Down Expand Up @@ -42,6 +45,40 @@ def tidy_stacktrace(strace):
trace.append((s[0], s[1], s[2], s[3]))
return trace

def get_template_info(source, context_lines=3):
line = 0
upto = 0
source_lines = []
before = during = after = ""

origin, (start, end) = source
template_source = origin.reload()

for num, next in enumerate(linebreak_iter(template_source)):
if start >= upto and end <= next:
line = num
before = template_source[upto:start]
during = template_source[start:end]
after = template_source[end:next]
source_lines.append((num, template_source[upto:next]))
upto = next

top = max(1, line - context_lines)
bottom = min(len(source_lines), line + 1 + context_lines)

context = []
for num, content in source_lines[top:bottom]:
context.append({
'num': num,
'content': content,
'highlight': (num == line),
})

return {
'name': origin.name,
'context': context,
}

class DatabaseStatTracker(util.CursorDebugWrapper):
"""
Replacement for CursorDebugWrapper which stores additional information
Expand All @@ -60,6 +97,22 @@ def execute(self, sql, params=()):
_params = simplejson.dumps([force_unicode(x) for x in params])
except TypeError:
pass # object not JSON serializable

template_info = None
cur_frame = sys._getframe().f_back
try:
while cur_frame is not None:
if cur_frame.f_code.co_name == 'render':
node = cur_frame.f_locals['self']
if isinstance(node, Node):
template_info = get_template_info(node.source)
break
cur_frame = cur_frame.f_back
except:
pass
finally:
del cur_frame

# We keep `sql` to maintain backwards compatibility
self.db.queries.append({
'sql': self.db.ops.last_executed_query(self.cursor, sql, params),
Expand All @@ -72,6 +125,7 @@ def execute(self, sql, params=()):
'stop_time': stop,
'is_slow': (duration > SQL_WARNING_THRESHOLD),
'is_select': sql.lower().strip().startswith('select'),
'template_info': template_info,
})
util.CursorDebugWrapper = DatabaseStatTracker

Expand Down
11 changes: 11 additions & 0 deletions debug_toolbar/templates/debug_toolbar/panels/sql.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@
</tr>
{% endfor %}
</table>
{% if query.template_info %}
<table>
{% for line in query.template_info.context %}
<tr>
<td>{{ line.num }}</td>
<td><pre style="font-family: monospace;{% if line.highlight %}background-color: lightgrey{% endif %}">{{ line.content }}</pre></td>
</tr>
{% endfor %}
</table>
<p><strong>{{ query.template_info.name|default:"(unknown)" }}</strong></p>
{% endif %}
</div>
{% endif %}
<span class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="width:{{ query.width_ratio }}%; left:{{ query.start_offset }}%;"></span>
Expand Down

0 comments on commit f6ada2c

Please sign in to comment.