Source

fat-po-editor / fat / debug_toolbar / views.py

"""
Helper views for the debug toolbar. These are dynamically installed when the
debug toolbar is displayed, and typically can do Bad Things, so hooking up these
views in any other way is generally not advised.
"""

import os
import django.views.static
from django.conf import settings
from django.db import connection
from django.http import HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.utils import simplejson
from django.utils.hashcompat import sha_constructor

class InvalidSQLError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

def debug_media(request, path):
    root = getattr(settings, 'DEBUG_TOOLBAR_MEDIA_ROOT', None)
    if root is None:
        parent = os.path.abspath(os.path.dirname(__file__))
        root = os.path.join(parent, 'media', 'debug_toolbar')
    return django.views.static.serve(request, path, root)

def sql_select(request):
    """
    Returns the output of the SQL SELECT statement.

    Expected GET variables:
        sql: urlencoded sql with positional arguments
        params: JSON encoded parameter values
        duration: time for SQL to execute passed in from toolbar just for redisplay
        hash: the hash of (secret + sql + params) for tamper checking
    """
    from debug_toolbar.panels.sql import reformat_sql
    sql = request.GET.get('sql', '')
    params = request.GET.get('params', '')
    hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()
    if hash != request.GET.get('hash', ''):
        return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert
    if sql.lower().strip().startswith('select'):
        params = simplejson.loads(params)
        cursor = connection.cursor()
        cursor.execute(sql, params)
        headers = [d[0] for d in cursor.description]
        result = cursor.fetchall()
        cursor.close()
        context = {
            'result': result,
            'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)),
            'duration': request.GET.get('duration', 0.0),
            'headers': headers,
        }
        return render_to_response('debug_toolbar/panels/sql_select.html', context)
    raise InvalidSQLError("Only 'select' queries are allowed.")

def sql_explain(request):
    """
    Returns the output of the SQL EXPLAIN on the given query.

    Expected GET variables:
        sql: urlencoded sql with positional arguments
        params: JSON encoded parameter values
        duration: time for SQL to execute passed in from toolbar just for redisplay
        hash: the hash of (secret + sql + params) for tamper checking
    """
    from debug_toolbar.panels.sql import reformat_sql
    sql = request.GET.get('sql', '')
    params = request.GET.get('params', '')
    hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()
    if hash != request.GET.get('hash', ''):
        return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert
    if sql.lower().strip().startswith('select'):
        params = simplejson.loads(params)
        cursor = connection.cursor()

        if settings.DATABASE_ENGINE == "sqlite3":
            # SQLite's EXPLAIN dumps the low-level opcodes generated for a query;
            # EXPLAIN QUERY PLAN dumps a more human-readable summary
            # See http://www.sqlite.org/lang_explain.html for details
            cursor.execute("EXPLAIN QUERY PLAN %s" % (sql,), params)
        else:
            cursor.execute("EXPLAIN %s" % (sql,), params)

        headers = [d[0] for d in cursor.description]
        result = cursor.fetchall()
        cursor.close()
        context = {
            'result': result,
            'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)),
            'duration': request.GET.get('duration', 0.0),
            'headers': headers,
        }
        return render_to_response('debug_toolbar/panels/sql_explain.html', context)
    raise InvalidSQLError("Only 'select' queries are allowed.")

def sql_profile(request):
    """
    Returns the output of running the SQL and getting the profiling statistics.

    Expected GET variables:
        sql: urlencoded sql with positional arguments
        params: JSON encoded parameter values
        duration: time for SQL to execute passed in from toolbar just for redisplay
        hash: the hash of (secret + sql + params) for tamper checking
    """
    from debug_toolbar.panels.sql import reformat_sql
    sql = request.GET.get('sql', '')
    params = request.GET.get('params', '')
    hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()
    if hash != request.GET.get('hash', ''):
        return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert
    if sql.lower().strip().startswith('select'):
        params = simplejson.loads(params)
        cursor = connection.cursor()
        result = None
        headers = None
        result_error = None
        try:
            cursor.execute("SET PROFILING=1") # Enable profiling
            cursor.execute(sql, params) # Execute SELECT
            cursor.execute("SET PROFILING=0") # Disable profiling
            # The Query ID should always be 1 here but I'll subselect to get the last one just in case...
            cursor.execute("SELECT * FROM information_schema.profiling WHERE query_id=(SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)")
            headers = [d[0] for d in cursor.description]
            result = cursor.fetchall()
        except:
            result_error = "Profiling is either not available or not supported by your database."
        cursor.close()
        context = {
            'result': result,
            'result_error': result_error,
            'sql': reformat_sql(cursor.db.ops.last_executed_query(cursor, sql, params)),
            'duration': request.GET.get('duration', 0.0),
            'headers': headers,
        }
        return render_to_response('debug_toolbar/panels/sql_profile.html', context)
    raise InvalidSQLError("Only 'select' queries are allowed.")

def template_source(request):
    """
    Return the source of a template, syntax-highlighted by Pygments if
    it's available.
    """
    from django.template import TemplateDoesNotExist
    from django.utils.safestring import mark_safe
    from django.conf import settings

    template_name = request.GET.get('template', None)
    if template_name is None:
        return HttpResponseBadRequest('"template" key is required')

    try: # Django 1.2 ...
        from django.template.loader import find_template_loader, make_origin
        loaders = []
        for loader_name in settings.TEMPLATE_LOADERS:
            loader = find_template_loader(loader_name)
            if loader is not None:
                loaders.append(loader)
        for loader in loaders:
            try:
                source, display_name = loader.load_template_source(template_name)
                origin = make_origin(display_name, loader, template_name, settings.TEMPLATE_DIRS)
                break
            except TemplateDoesNotExist:
                source = "Template Does Not Exist: %s" % (template_name,)
    except (ImportError, AttributeError): # Django 1.1 ...
        from django.template.loader import find_template_source
        source, origin = find_template_source(template_name)

    try:
        from pygments import highlight
        from pygments.lexers import HtmlDjangoLexer
        from pygments.formatters import HtmlFormatter

        source = highlight(source, HtmlDjangoLexer(), HtmlFormatter())
        source = mark_safe(source)
        source.pygmentized = True
    except ImportError:
        pass

    return render_to_response('debug_toolbar/panels/template_source.html', {
        'source': source,
        'template_name': template_name
    })