]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
(no commit message)
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Oct 2005 21:38:58 +0000 (21:38 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Oct 2005 21:38:58 +0000 (21:38 +0000)
examples/adjacencytree/byroot_tree.py
examples/adjacencytree/tables.py
lib/sqlalchemy/mapper.py
test/objectstore.py

index ac711eea2a491fb7bdb41e6dadca147e32d7bd50..e948c210360a29ea7c94621c7ba0c9b9701cb656 100644 (file)
@@ -52,6 +52,8 @@ class TreeNode(object):
             self.children.append(TreeNode(node))
         else:
             self.children.append(node)
+    def __repr__(self):
+        return self._getstring(0, False)
     def __str__(self):
         return self._getstring(0, False)
     def _getstring(self, level, expand = False):
@@ -89,6 +91,12 @@ class TreeLoader(MapperExtension):
             parentnode.children.append(instance, _mapper_nohistory=True)
         return False
             
+class TreeData(object):
+    def __init__(self, value=None):
+        self.id = None
+        self.value = value
+    def __repr__(self):
+        return "TreeData(%s, %s)" % (repr(self.id), repr(self.value))
 # define the mapper.  we will make "convenient" property
 # names vs. the more verbose names in the table definition
 
@@ -99,6 +107,8 @@ TreeNode.mapper=assignmapper(tables.trees, properties=dict(
     root_id=tables.trees.c.root_node_id,
     root=relation(TreeNode, primaryjoin=tables.trees.c.root_node_id==tables.trees.c.node_id, thiscol=tables.trees.c.root_node_id, lazy=None, uselist=False),
     children=relation(TreeNode, primaryjoin=tables.trees.c.parent_node_id==tables.trees.c.node_id, thiscol=tables.trees.c.node_id, lazy=None, uselist=True, private=True),
+    data=relation(TreeData, tables.treedata, properties=dict(id=tables.treedata.c.data_id), private=True, lazy=False)
+    
 ), extension = TreeLoader())
 TreeNode.mapper
 
@@ -109,7 +119,9 @@ node = TreeNode('rootnode')
 node.append('node1')
 node.append(node2)
 node.append('node3')
+node.children['node3'].data = TreeData('node 3s data')
 node.children['node2'].append('subnode2')
+node.children['node1'].data = TreeData('node 1s data')
 
 print "\n\n\n----------------------------"
 print "Created new tree structure:"
index 18286508787647f4e5fc9b9e2b64a481ae2d9f84..4c897fa8b264738d94199288e70a7a5529e64751 100644 (file)
@@ -11,11 +11,21 @@ trees = Table('treenodes', engine,
     Column('node_id', Integer, primary_key=True),
     Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True),
     Column('root_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True),
-    Column('node_name', String(50), nullable=False)
+    Column('node_name', String(50), nullable=False),
+    Column('data_ident', Integer, ForeignKey('treedata.data_id'))
     )
+
+treedata = Table(
+    "treedata", engine, 
+    Column('data_id', Integer, primary_key=True),
+    Column('value', String(100), nullable=False)
+)
     
 print "\n\n\n----------------------------"
 print "Creating Tree Table:"
 print "----------------------------"
-    
-trees.create()
\ No newline at end of file
+
+treedata.create()    
+trees.create()
+
+
index 39ce4bc0edfa84fa3772976a61f01f7fbb9b1a03..59411f13f3ad34adc07d1b4c1fc41e85ffc76993 100644 (file)
@@ -233,6 +233,7 @@ class Mapper(object):
             elif isinstance(prop, ColumnProperty):
                 prop.columns.append(column)
             else:
+                #print "WARNING: column %s not being added due to property %s" % (column.key, repr(prop))
                 continue
         
             # its a ColumnProperty - match the ultimate table columns
@@ -252,8 +253,10 @@ class Mapper(object):
 
     def _init_properties(self):
         [prop.init(key, self) for key, prop in self.props.iteritems()]
+        
     def __str__(self):
         return "Mapper|" + self.class_.__name__ + "|" + self.primarytable.name
+        
     def hash_key(self):
         return self.hashkey
 
@@ -310,6 +313,7 @@ class Mapper(object):
             except IndexError:
                 return None
 
+        
     def identity_key(self, *primary_keys):
         return objectstore.get_id_key(tuple(primary_keys), self.class_, self.primarytable)
     
@@ -352,7 +356,16 @@ class Mapper(object):
             return self._select_whereclause(arg, **params)
 
     def _getattrbycolumn(self, obj, column):
-        return self.columntoproperty[column][0].getattr(obj)
+        try:
+            prop = self.columntoproperty[column]
+        except KeyError:
+            try:
+                prop = self.props[column.key]
+                raise "Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))
+            except KeyError:
+                raise "No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self))
+                
+        return prop[0].getattr(obj)
 
     def _setattrbycolumn(self, obj, column, value):
         self.columntoproperty[column][0].setattr(obj, value)
@@ -710,7 +723,8 @@ class PropertyLoader(MapperProperty):
         else: 
             childlist = uow.attributes.get_history(obj, self.key)
         for child in childlist.deleted_items() + childlist.unchanged_items():
-            uow.register_deleted(child)
+            if child is not None:
+                uow.register_deleted(child)
 
             
     def register_dependencies(self, uowcommit):
@@ -748,15 +762,12 @@ class PropertyLoader(MapperProperty):
             return (obj2, obj1)
             
     def process_dependencies(self, task, deplist, uowcommit, delete = False):
-        print self.mapper.table.name + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete)
+        #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction)
 
         # fucntion to set properties across a parent/child object plus an "association row",
         # based on a join condition
         def sync_foreign_keys(binary):
-            if self.direction == PropertyLoader.RIGHT:
-                self._sync_foreign_keys(binary, child, obj, associationrow, clearkeys)
-            else:
-                self._sync_foreign_keys(binary, obj, child, associationrow, clearkeys)
+            self._sync_foreign_keys(binary, obj, child, associationrow, clearkeys)
         setter = BinaryVisitor(sync_foreign_keys)
 
         def getlist(obj, passive=True):
@@ -780,7 +791,6 @@ class PropertyLoader(MapperProperty):
                     uowcommit.register_deleted_list(childlist)
             else:
                 for obj in deplist:
-                    print "obj: " + repr(obj)
                     childlist = getlist(obj)
                     if childlist is None: continue
                     clearkeys = False
@@ -845,6 +855,45 @@ class PropertyLoader(MapperProperty):
         """given a binary clause with an = operator joining two table columns, synchronizes the values 
         of the corresponding attributes within a parent object and a child object, or the attributes within an 
         an "association row" that represents an association link between the 'parent' and 'child' object."""
+        # TODO: precompile this into a list of simple property setters.  this will allow checking for
+        # all the proper columns to be set up when the mapper is constructed and should simplify the code
+        if binary.operator == '=':
+            if binary.left.table == binary.right.table:
+                if binary.right is self.foreignkey:
+                    source = binary.left
+                elif binary.left is self.foreignkey:
+                    source = binary.right
+                else:
+                    raise "Cant determine direction for relationship %s = %s" % (binary.left.fullname, binary.right.fullname)
+                if self.direction == PropertyLoader.LEFT:
+                    self.mapper._setattrbycolumn(child, self.foreignkey, self.parent._getattrbycolumn(obj, source))
+                elif self.direction == PropertyLoader.RIGHT:
+                    self.parent._setattrbycolumn(obj, self.foreignkey, self.mapper._getattrbycolumn(child, source))
+                else:
+                    raise "assert failed"
+            else:
+                colmap = {binary.left.table : binary.left, binary.right.table : binary.right}
+                if colmap.has_key(self.parent.primarytable) and colmap.has_key(self.target):
+                    if self.direction == PropertyLoader.LEFT:
+                        print "set " + repr(child) + ":" + colmap[self.target].key + " to " + repr(obj) + ":" + colmap[self.parent.primarytable].key
+                        if clearkeys:
+                            self.mapper._setattrbycolumn(child, colmap[self.target], None)
+                        else:
+                            self.mapper._setattrbycolumn(child, colmap[self.target], self.parent._getattrbycolumn(obj, colmap[self.parent.primarytable]))
+                    elif self.direction == PropertyLoader.RIGHT:
+                        print "set " + repr(obj) + ":" + colmap[self.parent.primarytable].key + " to " + repr(child) + ":" + colmap[self.target].key
+                        if clearkeys:
+                            self.parent._setattrbycolumn(obj, colmap[self.parent.primarytable], None)
+                        else:
+                            self.parent._setattrbycolumn(obj, colmap[self.parent.primarytable], self.mapper._getattrbycolumn(child, colmap[self.target]))
+                    else:
+                        raise "assert failed"
+                elif colmap.has_key(self.parent.primarytable) and colmap.has_key(self.secondary):
+                    associationrow[colmap[self.secondary].key] = self.parent._getattrbycolumn(obj, colmap[self.parent.primarytable])
+                elif colmap.has_key(self.target) and colmap.has_key(self.secondary):
+                    associationrow[colmap[self.secondary].key] = self.mapper._getattrbycolumn(child, colmap[self.target])
+
+    def _compile_synchronizer(self, binary, obj, child, associationrow, clearkeys):
         if binary.operator == '=':
             if binary.left.table == binary.right.table:
                 if binary.right is self.foreignkey:
@@ -853,20 +902,35 @@ class PropertyLoader(MapperProperty):
                     source = binary.right
                 else:
                     raise "Cant determine direction for relationship %s = %s" % (binary.left.fullname, binary.right.fullname)
-                self.mapper._setattrbycolumn(child, self.foreignkey, self.parent._getattrbycolumn(obj, source))
+                if self.direction == PropertyLoader.LEFT:
+                    self.mapper._setattrbycolumn(child, self.foreignkey, self.parent._getattrbycolumn(obj, source))
+                elif self.direction == PropertyLoader.RIGHT:
+                    self.parent._setattrbycolumn(obj, self.foreignkey, self.mapper._getattrbycolumn(child, source))
+                else:
+                    raise "assert failed"
             else:
                 colmap = {binary.left.table : binary.left, binary.right.table : binary.right}
                 if colmap.has_key(self.parent.primarytable) and colmap.has_key(self.target):
-                    #print "set " + repr(child) + ":" + colmap[self.target].key + " to " + repr(obj) + ":" + colmap[self.parent.primarytable].key
-                    if clearkeys:
-                        self.mapper._setattrbycolumn(child, colmap[self.target], None)
+                    if self.direction == PropertyLoader.LEFT:
+                        print "set " + repr(child) + ":" + colmap[self.target].key + " to " + repr(obj) + ":" + colmap[self.parent.primarytable].key
+                        if clearkeys:
+                            self.mapper._setattrbycolumn(child, colmap[self.target], None)
+                        else:
+                            self.mapper._setattrbycolumn(child, colmap[self.target], self.parent._getattrbycolumn(obj, colmap[self.parent.primarytable]))
+                    elif self.direction == PropertyLoader.RIGHT:
+                        print "set " + repr(obj) + ":" + colmap[self.parent.primarytable].key + " to " + repr(child) + ":" + colmap[self.target].key
+                        if clearkeys:
+                            self.parent._setattrbycolumn(obj, colmap[self.parent.primarytable], None)
+                        else:
+                            self.parent._setattrbycolumn(obj, colmap[self.parent.primarytable], self.mapper._getattrbycolumn(child, colmap[self.target]))
                     else:
-                        self.mapper._setattrbycolumn(child, colmap[self.target], self.parent._getattrbycolumn(obj, colmap[self.parent.primarytable]))
+                        raise "assert failed"
                 elif colmap.has_key(self.parent.primarytable) and colmap.has_key(self.secondary):
                     associationrow[colmap[self.secondary].key] = self.parent._getattrbycolumn(obj, colmap[self.parent.primarytable])
                 elif colmap.has_key(self.target) and colmap.has_key(self.secondary):
                     associationrow[colmap[self.secondary].key] = self.mapper._getattrbycolumn(child, colmap[self.target])
 
+
     def execute(self, instance, row, identitykey, imap, isnew):
         pass
 
index a8b3cd112a58f073a64fdfe79aa1e1b73ade4250..8d8ffea21af0982f6e9002f6ecb0face36063eaa 100644 (file)
@@ -260,7 +260,6 @@ class SaveTest(AssertMixin):
         objects[2].email_address = 'imnew@foo.bar'
         objects[3].user = User()
         objects[3].user.user_name = 'imnewlyadded'
-        
         self.assert_sql(db, lambda: objectstore.uow().commit(), [
                 (
                     "INSERT INTO users (user_id, user_name) VALUES (:user_id, :user_name)",
@@ -276,7 +275,56 @@ class SaveTest(AssertMixin):
         ])
         l = sql.select([users, addresses], sql.and_(users.c.user_id==addresses.c.address_id, addresses.c.address_id==a.address_id)).execute()
         self.echo( repr(l.fetchone().row))
+
+    def testbackwardsnonmatch(self):
+        u2 = Table('users_nm', db,
+            Column('user_id', Integer, primary_key = True),
+            Column('user_name', String(20)),
+        )
+
+        a2 = Table('email_addresses_nm', db,
+            Column('address_id', Integer, primary_key = True),
+            Column('rel_user_id', Integer, ForeignKey(u2.c.user_id)),
+            Column('email_address', String(20)),
+        )
+        u2.create()
+        a2.create()
+        m = mapper(Address, a2, properties = dict(
+            user = relation(User, u2, lazy = True, uselist = False)
+        ))
+        data = [
+            {'user_name' : 'thesub' , 'email_address' : 'bar@foo.com'},
+            {'user_name' : 'assdkfj' , 'email_address' : 'thesdf@asdf.com'},
+        ]
+        objects = []
+        for elem in data:
+            a = Address()
+            a.email_address = elem['email_address']
+            a.user = User()
+            a.user.user_name = elem['user_name']
+            objects.append(a)
+        self.assert_sql(db, lambda: objectstore.commit(), [
+                (
+                    "INSERT INTO users_nm (user_id, user_name) VALUES (:user_id, :user_name)",
+                    {'user_id': None, 'user_name': 'thesub'}
+                ),
+                (
+                    "INSERT INTO users_nm (user_id, user_name) VALUES (:user_id, :user_name)",
+                    {'user_id': None, 'user_name': 'assdkfj'}
+                ),
+                (
+                "INSERT INTO email_addresses_nm (address_id, rel_user_id, email_address) VALUES (:address_id, :rel_user_id, :email_address)",
+                {'rel_user_id': 1, 'address_id': None, 'email_address': 'bar@foo.com'}
+                ),
+                (
+                "INSERT INTO email_addresses_nm (address_id, rel_user_id, email_address) VALUES (:address_id, :rel_user_id, :email_address)",
+                {'rel_user_id': 2, 'address_id': None, 'email_address': 'thesdf@asdf.com'}
+                )
+                ]
+        )
+        
         
+
     def testonetomany(self):
         """test basic save of one to many."""
         m = mapper(User, users, properties = dict(