]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
fixes to inheritance firing off dependency processors correctly + unittest
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 22 Jun 2006 22:23:57 +0000 (22:23 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 22 Jun 2006 22:23:57 +0000 (22:23 +0000)
CHANGES
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/inheritance.py

diff --git a/CHANGES b/CHANGES
index 0efbac11b6b51f8a35132bf8b4090af6e2b6df37..01925277956d9e629ca4933412806d9b9bce51a4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -15,6 +15,8 @@ the previous Session.
 - utterly remarkable:  added a single space between 'CREATE TABLE'
 and '(<the rest of it>' since *thats how MySQL indicates a non-
 reserved word tablename.....* [ticket:206]
+- more fixes to inheritance, related to many-to-many relations
+properly saving
 
 0.2.3
 - overhaul to mapper compilation to be deferred.  this allows mappers
index 067e842c35b4c2fd4497278aa2135ff362ddf46c..6eca75ae1db9a350c4d4fe082645a3a987f76f32 100644 (file)
@@ -85,7 +85,7 @@ class DependencyProcessor(object):
 class OneToManyDP(DependencyProcessor):
     def register_dependencies(self, uowcommit):
         if self.post_update:
-            stub = MapperStub(self.mapper)
+            stub = MapperStub(self.parent, self.mapper, self.key)
             uowcommit.register_dependency(self.mapper, stub)
             uowcommit.register_dependency(self.parent, stub)
             uowcommit.register_processor(stub, self, self.parent)
@@ -179,7 +179,7 @@ class OneToManyDP(DependencyProcessor):
 class ManyToOneDP(DependencyProcessor):
     def register_dependencies(self, uowcommit):
         if self.post_update:
-            stub = MapperStub(self.mapper)
+            stub = MapperStub(self.parent, self.mapper, self.key)
             uowcommit.register_dependency(self.mapper, stub)
             uowcommit.register_dependency(self.parent, stub)
             uowcommit.register_processor(stub, self, self.parent)
@@ -249,7 +249,7 @@ class ManyToManyDP(DependencyProcessor):
             # if we are the "backref" half of a two-way backref 
             # relationship, let the other mapper handle inserting the rows
             return
-        stub = MapperStub(self.mapper)
+        stub = MapperStub(self.parent, self.mapper, self.key)
         uowcommit.register_dependency(self.parent, stub)
         uowcommit.register_dependency(self.mapper, stub)
         uowcommit.register_processor(stub, self, self.parent)
@@ -304,7 +304,7 @@ class AssociationDP(OneToManyDP):
         # association/parent->stub->self, then we process the child
         # elments after the 'stub' save, which is before our own
         # mapper's save.
-        stub = MapperStub(self.association)
+        stub = MapperStub(self.parent, self.association, self.key)
         uowcommit.register_dependency(self.parent, stub)
         uowcommit.register_dependency(self.association, stub)
         uowcommit.register_dependency(stub, self.mapper)
@@ -371,7 +371,8 @@ class MapperStub(object):
     The Task objects in the objectstore module treat it just like
     any other Mapper, but in fact it only serves as a "dependency" placeholder
     for the many-to-many update task."""
-    def __init__(self, mapper):
+    __metaclass__ = util.ArgSingleton
+    def __init__(self, parent, mapper, key):
         self.mapper = mapper
         self._inheriting_mappers = []
     def register_dependencies(self, uowcommit):
index 846c7d125be6877a8da624d99535a8d679695910..6be5633932c4f877b03de2415cbc81a5942fe4e8 100644 (file)
@@ -315,7 +315,7 @@ class PropertyLoader(mapper.MapperProperty):
 
     def register_dependencies(self, uowcommit):
         self._dependency_processor.register_dependencies(uowcommit)
-
+            
     def _compile_synchronizers(self):
         """assembles a list of 'synchronization rules', which are instructions on how to populate
         the objects on each side of a relationship.  This is done when a PropertyLoader is 
index dcaf750dea78d350cb5a0ab7be70070896a13132..e243b3b313d3dc331eb7002903f051f5681d036e 100644 (file)
@@ -313,8 +313,6 @@ class UOWTransaction(object):
         this method returns or creates the single per-transaction instance of
         UOWTask that exists for that mapper."""
         try:
-            if isinstance(mapper, UOWTask):
-                raise "wha"
             return self.tasks[mapper]
         except KeyError:
             if dontcreate:
@@ -364,6 +362,11 @@ class UOWTransaction(object):
         #        print "TASK OBJ:", obj
         #    for elem in task.get_elements(polymorphic=True):
         #        print "POLYMORPHIC TASK OBJ:", elem.obj
+        
+        # insure that we have a UOWTask for every mapper that will be involved 
+        # in the topological sort
+        [self.get_task_by_mapper(m) for m in self._get_noninheriting_mappers()]
+        
         #print "-----------------------------"
         # pre-execute dependency processors.  this process may 
         # result in new tasks, objects and/or dependency processors being added,
index 0957638676bcdbbf0322ae29637bf2a5bfca68e0..842a63a26662c622a90751586ad8f35569e8442e 100644 (file)
@@ -412,6 +412,63 @@ class InheritTest6(testbase.AssertMixin):
         q = sess.query(Bar)
         self.assert_(len(q.selectfirst().lazy) == 1)
         self.assert_(len(q.selectfirst().eager) == 1)
-    
+
+
+class InheritTest7(testbase.AssertMixin):
+    """test dependency sorting among inheriting mappers"""
+    def setUpAll(self):
+        global users, roles, user_roles, admins, metadata
+        metadata=BoundMetaData(testbase.db)
+        users = Table('user', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('email', String(128)),
+            Column('password', String(16)),
+        )
+
+        roles = Table('role', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('description', String(32)) 
+        )
+
+        user_roles = Table('user_role', metadata,
+            Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
+            Column('role_id', Integer, ForeignKey('role.id'), primary_key=True)
+        )
+
+        admins = Table('admin', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('user_id', Integer, ForeignKey('user.id'))
+        )
+        metadata.create_all()
+    def tearDownAll(self):
+        metadata.drop_all()
+
+    def testbasic(self):
+        class User(object):pass
+        class Role(object):pass
+        class Admin(User):pass
+        role_mapper = mapper(Role, roles)
+        user_mapper = mapper(User, users, properties = {
+                'roles' : relation(Role, secondary=user_roles, lazy=False, private=False) 
+            }
+        )
+        admin_mapper = mapper(Admin, admins, inherits=user_mapper)
+        sess = create_session()
+        adminrole = Role('admin')
+        sess.save(adminrole)
+        sess.flush()
+
+        # create an Admin, and append a Role.  the dependency processors
+        # corresponding to the "roles" attribute for the Admin mapper and the User mapper
+        # have to insure that two dependency processors dont fire off and insert the
+        # many to many row twice.
+        a = Admin()
+        a.roles.append(adminrole)
+        a.password = 'admin'
+        sess.save(a)
+        sess.flush()
+        
+        assert user_roles.count().scalar() == 1
+        
 if __name__ == "__main__":    
     testbase.main()