From: Mike Bayer Date: Sun, 16 Oct 2005 21:38:58 +0000 (+0000) Subject: (no commit message) X-Git-Tag: rel_0_1_0~514 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3cb33357c41cedd2128c9d261208b33a370ad5d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git --- diff --git a/examples/adjacencytree/byroot_tree.py b/examples/adjacencytree/byroot_tree.py index ac711eea2a..e948c21036 100644 --- a/examples/adjacencytree/byroot_tree.py +++ b/examples/adjacencytree/byroot_tree.py @@ -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:" diff --git a/examples/adjacencytree/tables.py b/examples/adjacencytree/tables.py index 1828650878..4c897fa8b2 100644 --- a/examples/adjacencytree/tables.py +++ b/examples/adjacencytree/tables.py @@ -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() + + diff --git a/lib/sqlalchemy/mapper.py b/lib/sqlalchemy/mapper.py index 39ce4bc0ed..59411f13f3 100644 --- a/lib/sqlalchemy/mapper.py +++ b/lib/sqlalchemy/mapper.py @@ -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 diff --git a/test/objectstore.py b/test/objectstore.py index a8b3cd112a..8d8ffea21a 100644 --- a/test/objectstore.py +++ b/test/objectstore.py @@ -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(