[dabo-dev] dabo Commit 7278

Author: Nate Lowrie

Posted: 2012-10-13 at 13:39:17

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: Dabo-dev@mail.leafe.com

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/20121013183917.73867318B0B@mail.paulmcnett.com

©2012 Nate Lowrie