Russell Keith-Magee wrote:
> On 8/13/07, George Vilches <g
...@thataddress.com> wrote:
>> Russell Keith-Magee wrote:
>>> The configuration option will need to be a little more generic - i.e.,
>>> putting the entire backend into a record mode - not just a single
>>> cursor call.
>> Second, we could add a class level variable to each DatabaseWrapper,
>> since the handle to those seem to be instantiated only once at runtime
>> (at least, my short testing with just the Django built-in webserver
>> seemed to do so, I only assume that for Apache it's once per thread).
>> That would be an easy enough variable to update from pretty much
>> anywhere in the app:
>> connection.playback_only = True
> There's another possibility you haven't considered - dynamically
> replacing/wrapping the connection object. The test system already does
> this for the email framework - when the test framework is set up, the
> email framework is dynamically replaced with a mock; when the test
> framework is torn down, the mock is uninstalled. A similar approach
> could be used to 'start/end SQL recording'.
Since Adrian has done great work tonight landing Brian Harring's
database refactoring, it seemed like a good time to revisit this,
because dropping the addition of "non-executing" or "playback only" SQL
(what do we want to call this?) will be in a single place and very painless.
As far as dynamically wrapping it, that's a neat idea, and I think it
would have been very appropriate before the refactor. Now, we've
already got a debug cursor, and we're already tracking queries, so the
change seems very natural.
The two needed file patches are at the bottom of the e-mail. This
change is small and totally non-destructive to existing apps. If you
want to turn your DB to playback_only, you just have to call:
from django.db import connection
connection.playback_only = True
And since the connection variable are only be initialized once per
runtime, setting it once anywhere means that the app is in playback only
mode for the rest of the run. This could easily be controlled from a
middleware, so all the items below this middleware could be playback
only, but outer middlewares can still write to the database if necessary.
The only way this could be simpler or easier for the user to control is
if there was an entry in settings.py that the user could also use.
settings.DATABASE_PLAYBACK_ONLY, maybe? I'm only +0 on this. It would
have to be optional, because I can definitely see a reason for wanting
to turn this ability on and off during runtime without changing the code
at all. Specifically, I could imagine several setup programs in the
style of phpBB (I know, it's a sin to mention PHP apps on here, but
everything I can think of right this second is in PHP) that might want
to turn it off based on whether the user asked to display the queries or
actually execute them on install on the previous web page.
How do people feel about this approach?
Thanks,
gav
--- django_orig/django/db/backends/__init__.py 2007-08-19
21:22:44.000000000 -0400
+++ django_live/django/db/backends/__init__.py 2007-08-19
22:03:11.000000000 -0400
@@ -12,6 +12,7 @@ class BaseDatabaseWrapper(local):
ops = None
def __init__(self, **kwargs):
self.connection = None
+ self.playback_only = False
self.queries = []
self.options = kwargs
@@ -31,13 +32,13 @@ class BaseDatabaseWrapper(local):
def cursor(self):
from django.conf import settings
cursor = self._cursor(settings)
- if settings.DEBUG:
+ if settings.DEBUG or self.playback_only:
return self.make_debug_cursor(cursor)
return cursor
def make_debug_cursor(self, cursor):
from django.db.backends import util
- return util.CursorDebugWrapper(cursor, self)
+ return util.CursorDebugWrapper(cursor, self,
playback_only=self.playback_only)
class BaseDatabaseOperations(object):
"""
--- django_orig/django/db/backends/util.py 2007-08-19
21:22:44.000000000 -0400
+++ django_live/django/db/backends/util.py 2007-08-19
21:31:12.000000000 -0400
@@ -9,14 +9,16 @@ except ImportError:
from django.utils import _decimal as decimal # for Python 2.3
class CursorDebugWrapper(object):
- def __init__(self, cursor, db):
+ def __init__(self, cursor, db, playback_only=False):
self.cursor = cursor
self.db = db
+ self.allow_execute = not playback_only
def execute(self, sql, params=()):
start = time()
try:
- return self.cursor.execute(sql, params)
+ if self.allow_execute:
+ return self.cursor.execute(sql, params)
finally:
stop = time()
self.db.queries.append({
@@ -27,7 +29,8 @@ class CursorDebugWrapper(object):
def executemany(self, sql, param_list):
start = time()
try:
- return self.cursor.executemany(sql, param_list)
+ if self.allow_execute:
+ return self.cursor.executemany(sql, param_list)
finally:
stop = time()
self.db.queries.append({