Appending the request URL to SQL statements in Django

import sys

from django.http import HttpRequest
from django.db.backends import BaseDatabaseWrapper, util

class URLCursor(util.CursorDebugWrapper):
    def execute(self, sql, *args):
        f = sys._getframe()
        while f:
            request = f.f_locals.get('request')
            if isinstance(request, HttpRequest):
                sql += ' -- %s' % repr(request.path)[2:-1].replace('%', '%%')
            f = f.f_back

        return self.cursor.execute(sql, *args)

fn = BaseDatabaseWrapper.cursor
BaseDatabaseWrapper.cursor = lambda self: URLCursor(fn(self), self)

Now your SQL statements look like:

SELECT "auth_user"."id", [..] WHERE "auth_user"."id" = 1 -- /login

This allows you to quickly go from a misbehaving query in your production environment to the view that is executing it.

Comments (3)



June 1, 2010, 6:29 p.m. #

Wow.. that looks like a hackisch way to get the request. Seems like a thing that'seasy to abuse :S

June 2, 2010, 2:33 p.m. #
Care to elaborate?
Jeremy Dunck

This sort of functionality is why I've argued there should be some blackboard (… ) support in Django. Being able to refer to (but not modify) things like request can only be useful.

More specifically, since execute will be called repeatedly, and since BaseDatabaseWrapper.cursor is called at least once per request, you could stash the results of your frame hacking to boost performance on later SQL queries.

June 3, 2010, 8:03 p.m. #
That's a good idea. Difficult to know where to put the cached value though; perhaps a thread-local object that gets cleared by the request_finished signal.