dabo Commit
Revision 6866
Date: 2011-10-02 15:15:46 -0700 (Sun, 02 Oct 2011)
Author: Ed
Trac: http://trac.dabodev.com/changeset/6866
Changed:
U trunk/dabo/biz/dBizobj.py
U trunk/dabo/biz/test/test_mm.py
Log:
Modified the M-M calls so that they are wrapped in a transaction whenever
possible. Added the removeMMBizobj() method to un-associate a M-M bizobj.
Diff:
Modified: trunk/dabo/biz/dBizobj.py
===================================================================
--- trunk/dabo/biz/dBizobj.py 2011-09-30 23:47:51 UTC (rev 6865)
+++ trunk/dabo/biz/dBizobj.py 2011-10-02 22:15:46 UTC (rev 6866)
@@ -34,6 +34,7 @@
self.__cursors = {}
# PK of the currently-selected cursor
self.__currentCursorKey = None
+ # Description of the data represented by this bizobj
self._dataStructure = None
# Dictionary holding any default values to apply when a new
record is created. This is
@@ -325,7 +326,7 @@
self.afterLast()
- def beginTransaction(self):
+ def beginTransaction(self, crs=None):
"""
Attempts to begin a transaction at the database level, and
returns
True/False depending on its success.
@@ -333,13 +334,15 @@
rp = self._RemoteProxy
if rp:
return rp.beginTransaction()
+ if crs is None:
+ crs = self._CurrentCursor
ret = self._getTransactionToken()
if ret:
- self._CurrentCursor.beginTransaction()
+ crs.beginTransaction()
return ret
- def commitTransaction(self):
+ def commitTransaction(self, crs=None):
"""
Attempts to commit a transaction at the database level, and
returns
True/False depending on its success.
@@ -347,13 +350,15 @@
rp = self._RemoteProxy
if rp:
return rp.commitTransaction()
- ret = self._hasTransactionToken() and
self._CurrentCursor.commitTransaction()
+ if crs is None:
+ crs = self._CurrentCursor
+ ret = self._hasTransactionToken() and crs.commitTransaction()
if ret:
self._releaseTransactionToken()
return ret
- def rollbackTransaction(self):
+ def rollbackTransaction(self, crs=None):
"""
Attempts to rollback a transaction at the database level, and
returns
True/False depending on its success.
@@ -361,7 +366,9 @@
rp = self._RemoteProxy
if rp:
return rp.rollbackTransaction()
- ret = self._hasTransactionToken() and
self._CurrentCursor.rollbackTransaction()
+ if crs is None:
+ crs = self._CurrentCursor
+ ret = self._hasTransactionToken() and crs.rollbackTransaction()
if ret:
self._releaseTransactionToken()
return ret
@@ -1692,6 +1699,14 @@
"cursor": crs}
+ def removeMMBizobj(self, mmBizobj):
+ """
+ Removes the specified bizobj from a Many-to-Many relationship.
If no such
+ relationship exists, nothing happens.
+ """
+ assoc = self._associations.pop(mmBizobj.DataSource, None)
+
+
def getAncestorByDataSource(self, ds):
"""
Given a DataSource, finds the ancestor (parent, grandparent,
etc.) of
@@ -2096,14 +2111,32 @@
return None
+ def _mmAssociatedDbCall(self, bizOrDS, method_name, *args, **kwargs):
+ """
+ Wraps the call to an associated cursor so that wraps in a
transaction,
+ if at all possible.
+ """
+ assoc = self._getAssociation(bizOrDS)
+ crs = assoc["cursor"]
+ method = getattr(crs, method_name)
+ startTransaction = self.beginTransaction(crs)
+ try:
+ ret = method(*args, **kwargs)
+ except dException.DBQueryException:
+ if startTransaction:
+ self.rollbackTransaction(crs)
+ raise
+ self.commitTransaction(crs)
+ return ret
+
+
def mmAssociateValue(self, bizOrDS, otherField, otherVal):
"""
Associates the value in the 'other' table of a M-M relationship
with the
current record in the bizobj. If that value doesn't exist in
the other
table, it is added.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmAssociateValue(otherField, otherVal)
+ return self._mmAssociatedDbCall(bizOrDS, "mmAssociateValue",
otherField, otherVal)
def mmAssociateValues(self, bizOrDS, otherField, listOfValues):
@@ -2111,8 +2144,7 @@
Adds association records so that the current record in this
bizobj is associated
with every item in listOfValues. Other existing relationships
are unaffected.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmAssociateValues(otherField, listOfValues)
+ return self._mmAssociatedDbCall(bizOrDS, "mmAssociateValues",
otherField, listOfValues)
def mmDisssociateValue(self, bizOrDS, otherField, otherVal):
@@ -2121,8 +2153,7 @@
in the 'other' table of a M-M relationship. If no such
association exists,
nothing happens.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmDisssociateValue(otherField, otherVal)
+ return self._mmAssociatedDbCall(bizOrDS, "mmDisssociateValue",
otherField, otherVal)
def mmDisssociateValues(self, bizOrDS, otherField, listOfValues):
@@ -2131,8 +2162,7 @@
in the 'other' table of a M-M relationship. If no such
association exists,
nothing happens.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmDisssociateValues(otherField, listOfValues)
+ return self._mmAssociatedDbCall(bizOrDS, "mmDisssociateValues",
otherField, listOfValues)
def mmDisssociateAll(self, bizOrDS):
@@ -2140,8 +2170,7 @@
Removes all associations between the current record and the
associated
M-M table.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmDisssociateAll()
+ return self._mmAssociatedDbCall(bizOrDS, "mmDisssociateAll")
def mmSetFullAssociation(self, bizOrDS, otherField, listOfValues):
@@ -2149,8 +2178,7 @@
Adds and/or removes association records so that the current
record in this
bizobj is associated with every item in listOfValues, and none
other.
"""
- assoc = self._getAssociation(bizOrDS)
- assoc["cursor"].mmSetFullAssociation(otherField, listOfValues)
+ return self._mmAssociatedDbCall(bizOrDS,
"mmSetFullAssociation", otherField, listOfValues)
def mmAddToBoth(self, bizOrDS, thisField, thisVal, otherField,
otherVal):
@@ -2160,8 +2188,7 @@
both values exist in their respective tables, and will create
the
entry in the association table.
"""
- assoc = self._getAssociation(bizOrDS)
- return assoc["cursor"].mmAddToBoth(thisField, thisVal,
otherField, otherVal)
+ return self._mmAssociatedDbCall(bizOrDS, "mmAddToBoth",
thisField, thisVal, otherField, otherVal)
def mmGetAssociatedValues(self, bizOrDS, listOfFields):
@@ -2172,8 +2199,7 @@
"""
if not isinstance(listOfFields, (list, tuple)):
listOfFields = [listOfFields]
- assoc = self._getAssociation(bizOrDS)
- return assoc["cursor"].mmGetAssociatedValues(listOfFields)
+ return self._mmAssociatedDbCall(bizOrDS,
"mmGetAssociatedValues", listOfFields)
########## SQL Builder interface section ##############
Modified: trunk/dabo/biz/test/test_mm.py
===================================================================
--- trunk/dabo/biz/test/test_mm.py 2011-09-30 23:47:51 UTC (rev 6865)
+++ trunk/dabo/biz/test/test_mm.py 2011-10-02 22:15:46 UTC (rev 6866)
@@ -21,6 +21,10 @@
fan_club.KeyField = "pkid"
fan_club.DataSource = "fan_club"
fan_club.requery()
+ self.restricted_biz = dabo.biz.dBizobj(self.conn)
+ self.restricted_biz.KeyField = "pkid"
+ self.restricted_biz.DataSource = "restricted"
+ self.restricted_biz.requery()
# Set the MM relations
pbiz.addMMBizobj(self.company_biz, "employees", "person_id",
"company_id")
pbiz.addMMBizobj(self.fan_club_biz, "membership", "person_id",
"fan_club_id")
@@ -45,7 +49,12 @@
self.crs.execute("insert into fan_club (performer) values
('Ramones')")
self.crs.execute("insert into fan_club (performer) values ('Pat
Boone')")
+ # Table with NOT NULL restriction.
+ self.crs.execute("create table restricted (pkid INTEGER PRIMARY
KEY AUTOINCREMENT, regular TEXT, nonull TEXT NOT NULL);")
+ self.crs.execute("create table rest_alloc (pkid INTEGER PRIMARY
KEY AUTOINCREMENT, person_id INT, restricted_id INT);")
+ self.crs.execute("insert into restricted (regular, nonull)
values ('some_value', 'another_value')")
+
def reccount(self, tbl, filt=None):
"""Please note that SQL injection is consciously ignored here.
These
are in-memory tables!!
@@ -304,7 +313,28 @@
self.assertEqual(len(recs), 0)
+ def test_add_remove_mm_relationship(self):
+ """Ensures that addMMBizobj() and removeMMBizobj() work
correctly."""
+ pbiz = self.person_biz
+ rbiz = self.restricted_biz
+ num_orig_assoc = len(pbiz._associations)
+ pbiz.addMMBizobj(rbiz, "rest_alloc", "person_id",
"restricted_id")
+ self.assertEqual(len(pbiz._associations), num_orig_assoc + 1)
+ pbiz.removeMMBizobj(rbiz)
+ self.assertEqual(len(pbiz._associations), num_orig_assoc)
+
+ def test_db_insert_fails(self):
+ """If adding a value is not successful, ensure that the proper
error is raised."""
+ pbiz = self.person_biz
+ rbiz = self.restricted_biz
+ pbiz.addMMBizobj(rbiz, "rest_alloc", "person_id",
"restricted_id")
+ self.assertRaises(dException.DBQueryException,
pbiz.mmAssociateValue,
+ rbiz, "regular", "test")
+ pbiz.removeMMBizobj(rbiz)
+
+
+
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Many_To_Many)
unittest.TextTestRunner(verbosity=2).run(suite)
_______________________________________________
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]