dabo Commit
Revision 7278
Date: 2012-10-13 11:39:16 -0700 (Sat, 13 Oct 2012)
Author: Nate
Trac: http://trac.dabodev.com/changeset/7278
Changed:
U trunk/dabo/biz/dBizobj.py
U trunk/dabo/db/dBackend.py
U trunk/dabo/db/dConnectInfo.py
U trunk/dabo/db/dConnection.py
U trunk/dabo/db/dbMySQL.py
Log:
MySQL will auto-close connections after a certain period. If you try to
execute a statement, it will through a 2006 operational error. You can call
the connections ping method to reestablish the connection and try to execute
again. I wanted to overwrite the standard dabo cursor (dCursorMixin) with a
MySQL specific cursor. However this wasn't possible without a framework
change. We already had a facility in place to grab the specific backend dbapi
cursor class from the dBackend object. I extended that model so that instead
of hardcoding the main dCursorMixin class in the object we can grab it from the
dBackend object. It will auto-default to dCursorMixin if we don't override it
in the subclass.
Tested with MySQL and the SQLite adapter and it works. We should really
consider moving the CreateCursor and _getCursorClass methods on dBizobj down
into the db layer?\226?\128?\166
Diff:
Modified: trunk/dabo/biz/dBizobj.py
===================================================================
--- trunk/dabo/biz/dBizobj.py 2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/biz/dBizobj.py 2012-10-13 18:39:16 UTC (rev 7278)
@@ -21,8 +21,6 @@
class dBizobj(dObject):
""" The middle tier, where the business logic resides."""
- # Class to instantiate for the cursor object
- dCursorMixinClass = dCursorMixin
# Tell dObject that we'll call before and afterInit manually:
_call_beforeInit, _call_afterInit, _call_initProperties = False, False,
False
@@ -131,6 +129,7 @@
if conn:
# Base cursor class : the cursor class from the db api
self.dbapiCursorClass =
self._cursorFactory.getDictCursorClass()
+ self.dCursorMixinClass =
self._cursorFactory.getMainCursorClass()
# If there are any problems in the createCursor
process, an
# exception will be raised in that method.
self.createCursor()
@@ -459,7 +458,7 @@
# in some out-of-context child cursor, but that should be rare.
In the
# common case, all the changes would have already been made in
the above
# block, and isAnyChanged() will return False very quickly in
that case.
- if self.isAnyChanged():
+ if self.isAnyChanged():
try:
self.scan(self.save, startTransaction=False,
saveTheChildren=saveTheChildren, scanRequeryChildren=False)
@@ -561,7 +560,7 @@
# in some out-of-context child cursor, but that should be rare.
In the
# common case, all the cancellations would have already
happened in the
# above block, and isAnyChanged() will return False very
quickly.
- if self.isAnyChanged():
+ if self.isAnyChanged():
self.scanChangedRows(self.cancel, allCursors=False,
includeNewUnchanged=True,
cancelTheChildren=cancelTheChildren,
ignoreNoRecords=ignoreNoRecords,
reverse=True)
@@ -1313,7 +1312,7 @@
If the operator is specified, it will be used literally in the
evaluation instead of the
equals sign, unless it is one of the following strings, which
will be interpreted
as indicated:
-
+
| eq, equals: =
| ne, nequals: !=
| gt: >
@@ -1355,9 +1354,9 @@
"""Allows you to filter by any valid Python expression.
Use the field alias names, for example::
-
+
biz.filterByExpression('cust_name[0].lower() = 'a')
-
+
where cust_name is a field alias name in this record.
"""
self._CurrentCursor.filterByExpression(expr)
@@ -1444,7 +1443,7 @@
an error message that describes the reason why the data is not
valid; this message will be propagated back up to the UI where
it can
be displayed to the user so that they can correct the problem.
-
+
Example::
if not myNonEmptyField:
@@ -1515,7 +1514,7 @@
For internal use only! Should never be called from a
developer's code.
Its purpose is to keep child cursor in sync with parent cursor.
The updateChildren parameter meaning:
-
+
| None - the fastest one, doesn't update parent nor
requery child cursor
| False - update child cursor with current parent
| True - do both, update child cursor's parent and
requery child cursor.
@@ -1598,7 +1597,7 @@
If incremental is True (default is False), then we only compare
the first
characters up until the length of val.
-
+
Returns the RowNumber of the found record, or -1 if no match
found.
"""
ret = self._CurrentCursor.seek(val, fld, caseSensitive, near,
@@ -2117,7 +2116,7 @@
"""
Gets the structure of the DataSource table. Returns a list
of 3-tuples, where the 3-tuple's elements are:
-
+
| 0: the field name (string)
| 1: the field type ('I', 'N', 'C', 'M', 'B', 'D', 'T')
| 2: boolean specifying whether this is a pk field.
@@ -2129,7 +2128,7 @@
"""
Gets the structure of the DataSource table. Returns a list
of 3-tuples, where the 3-tuple's elements are:
-
+
| 0: the field name (string)
| 1: the field type ('I', 'N', 'C', 'M', 'B', 'D', 'T')
| 2: boolean specifying whether this is a pk field.
@@ -2576,7 +2575,7 @@
Hook method called after a field's value has been set.
Your hook method needs to accept two arguments:
-
+
| -> fld : The name of the changed field.
| -> row : The RowNumber of the changed field.
@@ -3105,7 +3104,7 @@
_("""If set, treated as cursor real table name where
DataSource
is an alias for it. This allows coexistence of many
business objects
with same data source on single form. (str)
-
+
Example:
class StockBase(dBizobj):
def initProperties(self):
Modified: trunk/dabo/db/dBackend.py
===================================================================
--- trunk/dabo/db/dBackend.py 2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dBackend.py 2012-10-13 18:39:16 UTC (rev 7278)
@@ -12,9 +12,9 @@
from dabo.db import dTable
from dNoEscQuoteStr import dNoEscQuoteStr
from dabo.lib.utils import ustr
+from dCursorMixin import dCursorMixin
-
class dBackend(dObject):
"""Abstract class inherited by the specific Dabo database connectors."""
# Pattern for determining if a function is present in a string
@@ -53,7 +53,11 @@
"""override in subclasses"""
return None
+ def getMainCursorClass(self):
+ """override in subclasses if they need something other than
dCursorMixin"""
+ return dCursorMixin
+
def getCursor(self, cursorClass):
"""override in subclasses if necessary"""
return cursorClass(self._connection)
Modified: trunk/dabo/db/dConnectInfo.py
===================================================================
--- trunk/dabo/db/dConnectInfo.py 2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dConnectInfo.py 2012-10-13 18:39:16 UTC (rev 7278)
@@ -86,6 +86,13 @@
return None
+ def getMainCursorClass(self):
+ try:
+ return self._backendObject.getMainCursorClass()
+ except TypeError:
+ return None
+
+
def encrypt(self, val):
if self.Application:
return self.Application.encrypt(val)
Modified: trunk/dabo/db/dConnection.py
===================================================================
--- trunk/dabo/db/dConnection.py 2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dConnection.py 2012-10-13 18:39:16 UTC (rev 7278)
@@ -44,6 +44,10 @@
return self._connectInfo.getDictCursorClass()
+ def getMainCursorClass(self):
+ return self._connectInfo.getMainCursorClass()
+
+
def getCursor(self, cursorClass):
return self.getBackendObject().getCursor(cursorClass)
Modified: trunk/dabo/db/dbMySQL.py
===================================================================
--- trunk/dabo/db/dbMySQL.py 2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dbMySQL.py 2012-10-13 18:39:16 UTC (rev 7278)
@@ -7,9 +7,19 @@
import dabo.dException as dException
from dNoEscQuoteStr import dNoEscQuoteStr as dNoEQ
from dabo.lib.utils import ustr
+from dCursorMixin import dCursorMixin
+class MySQLAutoReconnectCursor(dCursorMixin):
+ def execute(self, sql, params=None, errorClass=None,
convertQMarks=False):
+ from MySQLdb import OperationalError
+ try:
+ super(MySQLAutoReconnectCursor, self).execute(sql,
params=params, errorClass=OperationalError, convertQMarks=convertQMarks)
+ except OperationalError:
+ self.connection.ping(True)
+ super(MySQLAutoReconnectCursor, self).execute(sql,
params=params, errorClass=None, convertQMarks=convertQMarks)
+
class MySQL(dBackend):
"""Class providing MySQL connectivity. Uses MySQLdb."""
@@ -79,10 +89,19 @@
def getDictCursorClass(self):
- import MySQLdb.cursors as cursors
- return cursors.DictCursor
+ return MySQLdb.cursors.DictCursor
+ def getMainCursorClass(self):
+ return MySQLAutoReconnectCursor
+ #def getCursor(self, CursorClass):
+ # raise
+ # print "CursorClass is: %s" % CursorClass
+ # if CursorClass == dCrusorMixin:
+ # return MySQLAutoReconnectCursor(self._connection)
+ # else:
+ # return CursorClass(self._connection)
+
def beginTransaction(self, cursor):
""" Begin a SQL transaction."""
cursor.execute("START TRANSACTION")
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message:
http://leafe.com/archives/byMID/[email protected]