Skip to content

Instantly share code, notes, and snippets.

@epicserve
Last active March 23, 2021 11:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save epicserve/7253983 to your computer and use it in GitHub Desktop.
Save epicserve/7253983 to your computer and use it in GitHub Desktop.
A python context manager and decorator for debugging and optimizing queries for Django management commands.
<!DOCTYPE html>
<html>
<head>
<title>DB Stacktrace</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap -->
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="http://getbootstrap.com/docs-assets/css/pygments-manni.css" rel="stylesheet" media="screen">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
<style>
.stacktrace-row { display: none; }
</style>
</head>
<body>
<div class="container">
<h2>DB Stactrace</h2>
<div class="row">
<div class="col-md-12">
<table id="queries" class="table">
</table>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
<script>
$.getJSON('db-stacktrace.json', function (json) {
$.each(json, function() {
var data = this;
data['stacktrace'] = data['stacktrace'].replace(/line\s([\d]+)/g, 'line <span class="cm">$1</span>');
data['stacktrace'] = data['stacktrace'].replace(/File "(.+?)"/g, "$1");
data['stacktrace'] = data['stacktrace'].replace(/([\w\d]+)\.py,/g, '<span class="cm">$1.py,</span>');
var sql_tr = '<tr class="sql-row"><td class="num"><a href="#">' + data['num'] + '</a></td><td class="time">' + data['time'] + '</td><td class="sql">' + data['sql'] + '</td></tr>' + "\n";
var stacktrace = '<tr class="stacktrace-row"><td class="stacktrace" colspan="3"><div class="highlight"><pre>' + data['stacktrace'] + '</pre></div></td></tr>' + "\n";
$("#queries").append(sql_tr + stacktrace);
});
$('.sql-row .num a').click(function() {
$(this).parents('.sql-row').next().slideToggle();
return false;
});
});
</script>
</body>
</html>
from django.db import connections
from contextlib import contextmanager
import sqlparse
import re
import json
"""
A python context manager and decorator for debugging and optimizing queries
for Django management commands.
To view the JSON output, put the JSON file in the same directory as a template
like https://gist.github.com/epicserve/7253983#file-db-stacktrace-html. Then
``cd`` into the directory where the JSON file and the template are located and
run a simple http server like ``python -m SimpleHTTPServer 8000`` and then
view the file your browser at http://127.0.0.1:8000/.
Requirements:
- django-sql-stacktrace>=0.2.2
- sqlparse>=0.1.9
Example::
with queryinfo():
Mymodel.objects.all()
Example with json output::
with queryinfo(file_path='/Users/someuser/Desktop/db-stacktrace/db-stacktrace.json'):
Mymodel.objects.all()
"""
@contextmanager
def queryinfo(db_alias='default', file_path=None):
"""
Python context manager that prints all sql queries and the total number of
queries made within the current context.
"""
conn = connections[db_alias]
conn.use_debug_cursor = True
yield
queries = []
num = 1
for query in conn.queries:
query['stacktrace'] = None
query['num'] = num
m = re.match(r'^(.+?)\/\*(.+?)\*\/$', query['sql'], flags=re.S)
if m:
query['sql'] = sqlparse.format(m.group(1).strip(), reindent=True)
query['stacktrace'] = m.group(2).strip()
if file_path is None:
print("Query {num} ({time})".format(num=num, time=query['time']))
print(query['sql'])
if query['stacktrace'] is not None:
print(query['stacktrace'])
print("-" * 70)
queries.append(query)
num += 1
if file_path is None:
print("Total queries: {}".format(len(conn.queries)))
else:
with open(file_path, 'w') as f:
f.write(json.dumps(queries))
def debug_queries(db_alias='default', file_path=None):
"""
Python decorator that prints all sql queries and the total number of
queries made in the context of the method or function.
"""
def wrapper(f):
def inner_wrap(*args, **kwargs):
with queryinfo(db_alias, file_path):
f(*args, **kwargs)
return inner_wrap
if callable(db_alias) and file_path is None:
f = db_alias
db_alias = 'default'
return wrapper(f)
else:
return wrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment