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]

Reply via email to