Account Options

  1. Sign in
The old Google Groups will be going away soon.
Switch to the new Google Groups.
Google Groups Home
« Groups Home
Message from discussion Adding hooks to methods that generate SQL in django/core/management.py
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
George Vilches  
View profile  
 More options Aug 12 2007, 9:20 am
From: George Vilches <g...@thataddress.com>
Date: Sun, 12 Aug 2007 09:20:06 -0400
Local: Sun, Aug 12 2007 9:20 am
Subject: Re: Adding hooks to methods that generate SQL in django/core/management.py

Russell Keith-Magee wrote:
> I would suggest approaching this problem at lower than that -
> installing a filter at the level of the database cursor that diverts
> queries away from the actual database, and into a store. That way, if
> you run the code 'Author.objects.all()' , the backend will get the
> request to invoke 'SELECT * FROM Author', but this will get recorded
> rather than sent to the database.

> You then add a --sql flag to ./manage.py that sets up the recording
> mode on the database backend, and outputs the command buffer at the
> end of execution. If you make this interface generic, anyone could
> invoke SQL recording whenever they want.

> Part of this infrastructure is already in place for logging purposes.
> Improvements to the logging capability that allow for recording and
> playback would be most welcome.

How about the patch below?  When you create the cursor, if you want
access to "don't run this SQL, just have playback available", just use
connection.cursor(playback_only=True), and if you want to roll the
playback, you can either use self.db.queries directly, or use the
util.CursorDebugWrapper.playback method.  Totally backwards-compatible
and shouldn't step on any toes (uses existing logging, like you
mentioned above).  You can easily add this into
django/core/management.py.  If this is okay, I'll do a patch for all the
existing backends, it's a simple change now that I've looked through
everything, assuming there's no changes related to the next paragraph.

Something that I found a little irksome while working on this is that
the DatabaseWrapper class for each backend doesn't inherit from some
logical parent.  I know that all the db-specific functionality is
wrapped by these classes, but there are things that are in each class
that are most definitely shared functionality, like the mechanism by
which util.CursorDebugWrapper is instantiated.  We could move that off
to a method in a base class very nicely, and then the playback_only
addition wouldn't have to be added to every backend, or the change would
be more minimal.  Also, I think it might be appropriate to have
"playback()" be a method of the DatabaseWrapper (connection) and not the
CursorDebugWrapper, since if there's any DB-specific separators we need
to use, there's no way to deal with that from the cursor.  But, if we
don't make a base class, then we're going to have to duplicate
playback's code in all the backend/(db)/base.py files.  Not very DRY.

Thanks,
George

--- django_orig/django/db/backends/mysql/base.py        2007-08-02
20:59:29.000000000 -0400
+++ django_live/django/db/backends/mysql/base.py        2007-08-12
09:08:00.000000000 -0400
@@ -77,7 +77,7 @@ class DatabaseWrapper(local):
                  self.connection = None
          return False

-    def cursor(self):
+    def cursor(self, playback_only=False):
          from django.conf import settings
          from warnings import filterwarnings
          if not self._valid_connection():
@@ -103,9 +103,9 @@ class DatabaseWrapper(local):
              cursor = self.connection.cursor()
          else:
              cursor = self.connection.cursor()
-        if settings.DEBUG:
+        if settings.DEBUG or playback_only:
              filterwarnings("error", category=Database.Warning)
-            return util.CursorDebugWrapper(cursor, self)
+            return util.CursorDebugWrapper(cursor, self,
playback_only=playback_only)
          return cursor

      def _commit(self):

--- django_orig/django/db/backends/util.py      2007-08-02
20:59:29.000000000 -0400
+++ django_live/django/db/backends/util.py      2007-08-12
09:01:02.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({
@@ -40,6 +43,10 @@ class CursorDebugWrapper(object):
              return self.__dict__[attr]
          else:
              return getattr(self.cursor, attr)
+
+    def playback(self):
+        return ';'.join([query['sql'] for query in self.db.queries])
+

  def convert_args(args):
      """


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.