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('%', '%%')
                break
            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.

§

Tags: Computer Hacks Django
Planets: ALUG UWCS WUGLUG Debian

§

Three comments

  1. Genius.

    Robert

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

    Harro

    Care to elaborate?

    lamby

  3. This sort of functionality is why I've argued there should be some blackboard ( http://en.wi... ) 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.

    Jeremy Dunck

    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.

    lamby

Reply

§