sqlalchemy-commits Mailing List for SQLAlchemy (Page 359)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1487] sqlalchemy/branches/schema/examples/backref: factoring the "dependency processor" step into two steps; a pre-topological sort version and a post-topological sort version.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1487</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-22 14:34:44 -0500 (Mon, 22 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>factoring the "dependency processor" step into two steps; a pre-topological sort version and a post-topological sort version. this will help circularly-sorted flushes to work completely and will eventually remove the need for a possible "post-flush" task dump. sort_circular_dependencies simplified a little more (hooray).</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdatamappingtxt">sqlalchemy/branches/schema/doc/build/content/datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemaexamplesadjacencytreebasic_treepy">sqlalchemy/branches/schema/examples/adjacencytree/basic_tree.py</a></li> <li><a href="#sqlalchemybranchesschemaexamplesbackrefbackref_treepy">sqlalchemy/branches/schema/examples/backref/backref_tree.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormdependencypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentdatamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/datamapping.txt (1486 => 1487)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-22 17:33:50 UTC (rev 1486) +++ sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-22 19:34:44 UTC (rev 1487) </span><span class="lines">@@ -1,3 +1,6 @@ </span><ins>+[alpha_api]: javascript:alphaApi() +[alpha_implementation]: javascript:alphaImplementation() + </ins><span class="cx"> Data Mapping {@name=datamapping} </span><span class="cx"> ============ </span><span class="cx"> </span><span class="lines">@@ -424,6 +427,8 @@ </span><span class="cx"> </span><span class="cx"> #### More Granular Join Control Using join\_to, join\_via {@name=jointo} </span><span class="cx"> </span><ins>+Feature Status: [Alpha API][alpha_api] + </ins><span class="cx"> The `join_to` method of `Query` is a component of the `select_by` operation, and is given a keyname in order to return a "join path" from the Query's mapper to the mapper which is referenced by a `relation()` of the given name: </span><span class="cx"> </span><span class="cx"> {python} </span></span></pre></div> <a id="sqlalchemybranchesschemaexamplesadjacencytreebasic_treepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/adjacencytree/basic_tree.py (1486 => 1487)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/adjacencytree/basic_tree.py 2006-05-22 17:33:50 UTC (rev 1486) +++ sqlalchemy/branches/schema/examples/adjacencytree/basic_tree.py 2006-05-22 19:34:44 UTC (rev 1487) </span><span class="lines">@@ -4,47 +4,36 @@ </span><span class="cx"> </span><span class="cx"> """a basic Adjacency List model tree.""" </span><span class="cx"> </span><del>-engine = create_engine('sqlite://', echo = True) -#engine = sqlalchemy.engine.create_engine('mysql', {'db':'test', 'host':'127.0.0.1', 'user':'scott'}, echo=True) -#engine = sqlalchemy.engine.create_engine('postgres', {'database':'test', 'host':'127.0.0.1', 'user':'scott', 'password':'tiger'}, echo=True) -#engine = sqlalchemy.engine.create_engine('oracle', {'dsn':os.environ['DSN'], 'user':os.environ['USER'], 'password':os.environ['PASSWORD']}, echo=True) </del><ins>+metadata = BoundMetaData('sqlite:///', echo=True) </ins><span class="cx"> </span><ins>+"""create the treenodes table. This is a basic adjacency list model table.""" </ins><span class="cx"> </span><del>-"""create the treenodes table. This is ia basic adjacency list model table.""" - -trees = Table('treenodes', engine, </del><ins>+trees = Table('treenodes', metadata, </ins><span class="cx"> Column('node_id', Integer, Sequence('treenode_id_seq',optional=False), primary_key=True), </span><span class="cx"> Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), </span><span class="cx"> Column('node_name', String(50), nullable=False), </span><span class="cx"> ) </span><span class="cx"> </span><del>- </del><span class="cx"> class NodeList(util.OrderedDict): </span><del>- """extends an Ordered Dictionary, which is just a dictionary that returns its keys and values - in order upon iteration. Adds functionality to automatically associate - the parent of a TreeNode with itself, upon append to the parent's list of child nodes.""" - def __init__(self, parent): - util.OrderedDict.__init__(self) - self.parent = parent </del><span class="cx"> def append(self, node): </span><del>- node.parent = self.parent </del><span class="cx"> self[node.name] = node </span><span class="cx"> def __iter__(self): </span><span class="cx"> return iter(self.values()) </span><span class="cx"> </span><span class="cx"> class TreeNode(object): </span><span class="cx"> """a rich Tree class which includes path-based operations""" </span><del>- def __init__(self, name=None): - self.children = NodeList(self) </del><ins>+ children = NodeList + def __init__(self, name): + self.children = NodeList() </ins><span class="cx"> self.name = name </span><span class="cx"> self.parent = None </span><span class="cx"> self.id = None </span><span class="cx"> self.parent_id = None </span><span class="cx"> def append(self, node): </span><span class="cx"> if isinstance(node, str): </span><del>- self.children.append(TreeNode(node)) - else: - self.children.append(node) </del><ins>+ node = TreeNode(node) + node.parent = self + self.children.append(node) </ins><span class="cx"> def __repr__(self): </span><span class="cx"> return self._getstring(0, False) </span><span class="cx"> def __str__(self): </span><span class="lines">@@ -57,14 +46,11 @@ </span><span class="cx"> def print_nodes(self): </span><span class="cx"> return self._getstring(0, True) </span><span class="cx"> </span><del>-# define the mapper. we will make "convenient" property -# names vs. the more verbose names in the table definition - -assign_mapper(TreeNode, trees, properties=dict( </del><ins>+mapper(TreeNode, trees, properties=dict( </ins><span class="cx"> id=trees.c.node_id, </span><span class="cx"> name=trees.c.node_name, </span><span class="cx"> parent_id=trees.c.parent_node_id, </span><del>- children=relation(TreeNode, private=True), </del><ins>+ children=relation(TreeNode, private=True, backref=backref("parent", foreignkey=trees.c.node_id)), </ins><span class="cx"> )) </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="lines">@@ -88,10 +74,12 @@ </span><span class="cx"> print node.print_nodes() </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><del>-print "Committing:" </del><ins>+print "Flushing:" </ins><span class="cx"> print "----------------------------" </span><span class="cx"> </span><del>-objectstore.commit() </del><ins>+session = create_session(echo_uow=True) +session.save(node) +session.flush() </ins><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Tree After Save:" </span><span class="lines">@@ -114,9 +102,9 @@ </span><span class="cx"> print node.print_nodes() </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><del>-print "Committing:" </del><ins>+print "Flushing:" </ins><span class="cx"> print "----------------------------" </span><del>-objectstore.commit() </del><ins>+session.flush() </ins><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Tree After Save:" </span><span class="lines">@@ -127,12 +115,12 @@ </span><span class="cx"> nodeid = node.id </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><del>-print "Clearing objectstore, selecting " </del><ins>+print "Clearing session, selecting " </ins><span class="cx"> print "tree new where node_id=%d:" % nodeid </span><span class="cx"> print "----------------------------" </span><span class="cx"> </span><del>-objectstore.clear() -t = TreeNode.mapper.select(TreeNode.c.node_id==nodeid)[0] </del><ins>+session.clear() +t = session.query(TreeNode).select(TreeNode.c.id==nodeid)[0] </ins><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Full Tree:" </span><span class="lines">@@ -141,7 +129,7 @@ </span><span class="cx"> </span><span class="cx"> print "\n\n\n----------------------------" </span><span class="cx"> print "Marking root node as deleted" </span><del>-print "and committing:" </del><ins>+print "and flushing:" </ins><span class="cx"> print "----------------------------" </span><del>-objectstore.delete(t) -objectstore.commit() </del><ins>+session.delete(t) +session.flush() </ins></span></pre></div> <a id="sqlalchemybranchesschemaexamplesbackrefbackref_treepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/examples/backref/backref_tree.py (1486 => 1487)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/examples/backref/backref_tree.py 2006-05-22 17:33:50 UTC (rev 1486) +++ sqlalchemy/branches/schema/examples/backref/backref_tree.py 2006-05-22 19:34:44 UTC (rev 1487) </span><span class="lines">@@ -1,7 +1,6 @@ </span><span class="cx"> from sqlalchemy import * </span><del>-import sqlalchemy.attributes as attributes </del><span class="cx"> </span><del>-engine = create_engine('sqlite://', echo=True) </del><ins>+metadata = BoundMetaData('sqlite:///', echo=True) </ins><span class="cx"> </span><span class="cx"> class Tree(object): </span><span class="cx"> def __init__(self, name='', father=None): </span><span class="lines">@@ -12,27 +11,25 @@ </span><span class="cx"> def __repr__(self): </span><span class="cx"> return self.__str__() </span><span class="cx"> </span><del>-table = Table('tree', engine, </del><ins>+table = Table('tree', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> Column('name', String(64), nullable=False), </span><del>- Column('father_id', Integer, ForeignKey('tree.id'), nullable=True),) </del><ins>+ Column('father_id', Integer, ForeignKey('tree.id'), nullable=True)) +table.create() </ins><span class="cx"> </span><del>-assign_mapper(Tree, table, </del><ins>+mapper(Tree, table, </ins><span class="cx"> properties={ </span><del>- # set up a backref using a string - #'father':relation(Tree, foreignkey=table.c.id,primaryjoin=table.c.father_id==table.c.id, backref='childs')}, - - # or set up using the backref() function, which allows arguments to be passed - 'childs':relation(Tree, foreignkey=table.c.father_id, primaryjoin=table.c.father_id==table.c.id, backref=backref('father', uselist=False, foreignkey=table.c.id))}, </del><ins>+ 'childs':relation(Tree, foreignkey=table.c.father_id, primaryjoin=table.c.father_id==table.c.id, backref=backref('father', uselist=False, foreignkey=table.c.id))}, </ins><span class="cx"> ) </span><span class="cx"> </span><del>-table.create() </del><span class="cx"> root = Tree('root') </span><span class="cx"> child1 = Tree('child1', root) </span><span class="cx"> child2 = Tree('child2', root) </span><span class="cx"> child3 = Tree('child3', child1) </span><span class="cx"> </span><del>-objectstore.commit() </del><ins>+session = create_session() +session.save(root) +session.flush() </ins><span class="cx"> </span><span class="cx"> print root.childs </span><span class="cx"> print child1.childs </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormdependencypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py (1486 => 1487)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py 2006-05-22 17:33:50 UTC (rev 1486) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/dependency.py 2006-05-22 19:34:44 UTC (rev 1487) </span><span class="lines">@@ -105,6 +105,8 @@ </span><span class="cx"> raise AssertionError(" no foreign key ?") </span><span class="cx"> </span><span class="cx"> def get_object_dependencies(self, obj, uowcommit, passive = True): </span><ins>+ """returns the list of objects that are dependent on the given object, as according to the relationship + this dependency processor represents""" </ins><span class="cx"> return uowcommit.uow.attributes.get_history(obj, self.key, passive = passive) </span><span class="cx"> </span><span class="cx"> def whose_dependent_on_who(self, obj1, obj2): </span><span class="lines">@@ -120,19 +122,21 @@ </span><span class="cx"> return (obj2, obj1) </span><span class="cx"> </span><span class="cx"> def process_dependencies(self, task, deplist, uowcommit, delete = False): </span><del>- """this method is called during a commit operation to synchronize data between a parent and child object. - it also can establish child or parent objects within the unit of work as "to be saved" or "deleted" - in some cases.""" </del><ins>+ """this method is called during a flush operation to synchronize data between a parent and child object. + it is called within the context of the various mappers and sometimes individual objects sorted according to their + insert/update/delete order (topological sort).""" </ins><span class="cx"> #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction) </span><span class="cx"> </span><span class="cx"> def getlist(obj, passive=True): </span><span class="cx"> return self.get_object_dependencies(obj, uowcommit, passive) </span><span class="cx"> </span><del>- connection = uowcommit.transaction.connection(self.mapper) - </del><span class="cx"> # plugin point </span><span class="cx"> </span><ins>+ # TODO: process_dependencies has been refactored into two methods, process_dependencies and preprocess_dependencies. + # cleanup is still required to hone the method down to its minimal amount of code. + </ins><span class="cx"> if self.direction == MANYTOMANY: </span><ins>+ connection = uowcommit.transaction.connection(self.mapper) </ins><span class="cx"> secondary_delete = [] </span><span class="cx"> secondary_insert = [] </span><span class="cx"> if delete: </span><span class="lines">@@ -164,11 +168,7 @@ </span><span class="cx"> connection.execute(statement, secondary_insert) </span><span class="cx"> elif self.direction == MANYTOONE and delete: </span><span class="cx"> if self.cascade.delete_orphan: </span><del>- for obj in deplist: - childlist = getlist(obj, False) - for child in childlist.deleted_items() + childlist.unchanged_items(): - if child is not None and childlist.hasparent(child) is False: - uowcommit.register_object(child, isdelete=True) </del><ins>+ pass </ins><span class="cx"> elif self.post_update: </span><span class="cx"> # post_update means we have to update our row to not reference the child object </span><span class="cx"> # before we can DELETE the row </span><span class="lines">@@ -179,25 +179,20 @@ </span><span class="cx"> # head object is being deleted, and we manage its list of child objects </span><span class="cx"> # the child objects have to have their foreign key to the parent set to NULL </span><span class="cx"> if self.cascade.delete_orphan and not self.post_update: </span><del>- for obj in deplist: - childlist = getlist(obj, False) - for child in childlist.deleted_items(): - if child is not None and childlist.hasparent(child) is False: - uowcommit.register_object(child, isdelete=True) - for child in childlist.unchanged_items(): - if child is not None: - uowcommit.register_object(child, isdelete=True) </del><ins>+ pass </ins><span class="cx"> else: </span><span class="cx"> for obj in deplist: </span><span class="cx"> childlist = getlist(obj, False) </span><span class="cx"> for child in childlist.deleted_items(): </span><span class="cx"> if child is not None and childlist.hasparent(child) is False: </span><span class="cx"> self._synchronize(obj, child, None, True) </span><del>- uowcommit.register_object(child, postupdate=self.post_update) </del><ins>+ if self.post_update: + uowcommit.register_object(child, postupdate=True) </ins><span class="cx"> for child in childlist.unchanged_items(): </span><span class="cx"> if child is not None: </span><span class="cx"> self._synchronize(obj, child, None, True) </span><del>- uowcommit.register_object(child, postupdate=self.post_update) </del><ins>+ if self.post_update: + uowcommit.register_object(child, postupdate=True) </ins><span class="cx"> elif self.association is not None: </span><span class="cx"> # manage association objects. </span><span class="cx"> for obj in deplist: </span><span class="lines">@@ -240,21 +235,86 @@ </span><span class="cx"> if childlist is not None: </span><span class="cx"> for child in childlist.added_items(): </span><span class="cx"> self._synchronize(obj, child, None, False) </span><ins>+ if self.direction == ONETOMANY and child is not None and self.post_update: + uowcommit.register_object(child, postupdate=True) + if self.direction == MANYTOONE: + if self.post_update: + uowcommit.register_object(obj, postupdate=True) + else: + for child in childlist.deleted_items(): + if not self.cascade.delete_orphan: + self._synchronize(obj, child, None, True) + + def preprocess_dependencies(self, task, deplist, uowcommit, delete = False): + """used before the flushes' topological sort to traverse through related objects and insure every + instance which will require save/update/delete is properly added to the UOWTransaction.""" + #print self.mapper.table.name + " " + self.key + " " + repr(len(deplist)) + " process_dep isdelete " + repr(delete) + " direction " + repr(self.direction) + + # TODO: post_update instructions should be established in this step as well + # (and executed in the regular traversal) + if self.post_update: + return + + # TODO: this method is the product of splitting process_dependencies into two methods. + # cleanup is still required to hone the method down to its minimal amount of code. + + def getlist(obj, passive=True): + return self.get_object_dependencies(obj, uowcommit, passive) + + if self.direction == MANYTOMANY: + pass + elif self.direction == MANYTOONE and delete: + if self.cascade.delete_orphan: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items() + childlist.unchanged_items(): + if child is not None and childlist.hasparent(child) is False: + uowcommit.register_object(child, isdelete=True) + elif self.direction == ONETOMANY and delete: + # head object is being deleted, and we manage its list of child objects + # the child objects have to have their foreign key to the parent set to NULL + if self.cascade.delete_orphan and not self.post_update: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items(): + if child is not None and childlist.hasparent(child) is False: + uowcommit.register_object(child, isdelete=True) + for child in childlist.unchanged_items(): + if child is not None: + uowcommit.register_object(child, isdelete=True) + else: + for obj in deplist: + childlist = getlist(obj, False) + for child in childlist.deleted_items(): + if child is not None and childlist.hasparent(child) is False: + uowcommit.register_object(child) + for child in childlist.unchanged_items(): + if child is not None: + uowcommit.register_object(child) + elif self.association is not None: + # TODO: clean up the association step in process_dependencies and move the + # appropriate sections of it to here + pass + else: + for obj in deplist: + childlist = getlist(obj, passive=True) + if childlist is not None: + for child in childlist.added_items(): </ins><span class="cx"> if self.direction == ONETOMANY and child is not None: </span><del>- uowcommit.register_object(child, postupdate=self.post_update) </del><ins>+ uowcommit.register_object(child) </ins><span class="cx"> if self.direction == MANYTOONE: </span><del>- uowcommit.register_object(obj, postupdate=self.post_update) </del><ins>+ uowcommit.register_object(obj) </ins><span class="cx"> else: </span><span class="cx"> for child in childlist.deleted_items(): </span><span class="cx"> if not self.cascade.delete_orphan: </span><del>- self._synchronize(obj, child, None, True) </del><span class="cx"> uowcommit.register_object(child, isdelete=False) </span><span class="cx"> elif childlist.hasparent(child) is False: </span><span class="cx"> uowcommit.register_object(child, isdelete=True) </span><span class="cx"> </span><ins>+ </ins><span class="cx"> def _synchronize(self, obj, child, associationrow, clearkeys): </span><del>- """called during a commit to execute the full list of syncrules on the - given object/child/optional association row""" </del><ins>+ """called during a flush to synchronize primary key identifier values between a parent/child object, as well as + to an associationrow in the case of many-to-many.""" </ins><span class="cx"> if self.direction == ONETOMANY: </span><span class="cx"> source = obj </span><span class="cx"> dest = child </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1486 => 1487)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-22 17:33:50 UTC (rev 1486) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-22 19:34:44 UTC (rev 1487) </span><span class="lines">@@ -269,6 +269,7 @@ </span><span class="cx"> self.dependencies = {} </span><span class="cx"> self.tasks = {} </span><span class="cx"> self.__modified = False </span><ins>+ self._is_executing = False </ins><span class="cx"> </span><span class="cx"> def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, **kwargs): </span><span class="cx"> """adds an object to this UOWTransaction to be updated in the database. </span><span class="lines">@@ -298,7 +299,7 @@ </span><span class="cx"> # can put an assertion here to make sure.... </span><span class="cx"> if task.circular: </span><span class="cx"> return </span><del>- </del><ins>+ </ins><span class="cx"> mod = task.append(obj, listonly, isdelete=isdelete, **kwargs) </span><span class="cx"> self.__modified = self.__modified or mod </span><span class="cx"> </span><span class="lines">@@ -341,13 +342,17 @@ </span><span class="cx"> mapperfrom = mapperfrom._primary_mapper() </span><span class="cx"> task = self.get_task_by_mapper(mapper) </span><span class="cx"> targettask = self.get_task_by_mapper(mapperfrom) </span><del>- task.dependencies.append(UOWDependencyProcessor(processor, targettask, isdeletefrom)) </del><ins>+ up = UOWDependencyProcessor(processor, targettask, isdeletefrom) + task.dependencies.append(up) + up.preexecute(self) </ins><span class="cx"> self.__modified = True </span><span class="cx"> </span><span class="cx"> def execute(self, echo=False): </span><span class="cx"> for task in self.tasks.values(): </span><span class="cx"> task.mapper.register_dependencies(self) </span><span class="cx"> </span><ins>+ self._is_executing = True + </ins><span class="cx"> head = self._sort_dependencies() </span><span class="cx"> self.__modified = False </span><span class="cx"> if LOG or echo: </span><span class="lines">@@ -357,6 +362,8 @@ </span><span class="cx"> print "Task dump:\n" + head.dump() </span><span class="cx"> if head is not None: </span><span class="cx"> head.execute(self) </span><ins>+ #if self.__modified and head is not None: + # raise "Assertion failed ! new pre-execute dependency step should eliminate post-execute changes (except post_update stuff)." </ins><span class="cx"> if LOG or echo: </span><span class="cx"> if self.__modified and head is not None: </span><span class="cx"> print "\nAfter Execute:\n" + head.dump() </span><span class="lines">@@ -425,11 +432,17 @@ </span><span class="cx"> self.targettask = targettask </span><span class="cx"> self.isdeletefrom = isdeletefrom </span><span class="cx"> </span><del>- def execute(self, trans, delete): - if not delete: - self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.tosave_elements() if elem.obj is not None], trans, delete = delete) </del><ins>+ def preexecute(self, trans): + if not self.isdeletefrom: + self.processor.preprocess_dependencies(self.targettask, [elem.obj for elem in self.targettask.tosave_elements() if elem.obj is not None], trans, delete=self.isdeletefrom) </ins><span class="cx"> else: </span><del>- self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.todelete_elements() if elem.obj is not None], trans, delete = delete) </del><ins>+ self.processor.preprocess_dependencies(self.targettask, [elem.obj for elem in self.targettask.todelete_elements() if elem.obj is not None], trans, delete=self.isdeletefrom) + + def execute(self, trans): + if not self.isdeletefrom: + self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.tosave_elements() if elem.obj is not None], trans, delete=self.isdeletefrom) + else: + self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.todelete_elements() if elem.obj is not None], trans, delete=self.isdeletefrom) </ins><span class="cx"> </span><span class="cx"> def get_object_dependencies(self, obj, trans, passive): </span><span class="cx"> return self.processor.get_object_dependencies(obj, trans, passive=passive) </span><span class="lines">@@ -497,16 +510,16 @@ </span><span class="cx"> </span><span class="cx"> self.mapper.save_obj(self.tosave_objects(), trans) </span><span class="cx"> for dep in self.cyclical_save_dependencies(): </span><del>- dep.execute(trans, delete=False) </del><ins>+ dep.execute(trans) </ins><span class="cx"> for element in self.tosave_elements(): </span><span class="cx"> for task in element.childtasks: </span><span class="cx"> task.execute(trans) </span><span class="cx"> for dep in self.save_dependencies(): </span><del>- dep.execute(trans, delete=False) </del><ins>+ dep.execute(trans) </ins><span class="cx"> for dep in self.delete_dependencies(): </span><del>- dep.execute(trans, delete=True) </del><ins>+ dep.execute(trans) </ins><span class="cx"> for dep in self.cyclical_delete_dependencies(): </span><del>- dep.execute(trans, delete=True) </del><ins>+ dep.execute(trans) </ins><span class="cx"> for child in self.childtasks: </span><span class="cx"> child.execute(trans) </span><span class="cx"> for element in self.todelete_elements(): </span><span class="lines">@@ -550,14 +563,18 @@ </span><span class="cx"> cycles = Set(cycles) </span><span class="cx"> </span><span class="cx"> #print "BEGIN CIRC SORT-------" </span><ins>+ #print "PRE-CIRC:" + #print list(cycles)[0].dump() + </ins><span class="cx"> # dependency processors that arent part of the cyclical thing </span><span class="cx"> # get put here </span><span class="cx"> extradeplist = [] </span><span class="cx"> </span><span class="cx"> object_to_original_task = {} </span><span class="cx"> </span><del>- # this creates a map of UOWTasks mapped to a particular object - # and a particular dependency processor. </del><ins>+ # organizes a set of new UOWTasks that will be assembled into + # the final tree, for the purposes of holding new UOWDependencyProcessors + # which process small sub-sections of dependent parent/child operations </ins><span class="cx"> dependencies = {} </span><span class="cx"> def get_dependency_task(obj, depprocessor): </span><span class="cx"> try: </span><span class="lines">@@ -572,9 +589,7 @@ </span><span class="cx"> dp[depprocessor] = l </span><span class="cx"> return l </span><span class="cx"> </span><del>- # work out a list of all the "dependency processors" that - # represent objects that have to be dependency sorted at the - # per-object level. </del><ins>+ # organize all original UOWDependencyProcessors by their target task </ins><span class="cx"> deps_by_targettask = {} </span><span class="cx"> for task in cycles: </span><span class="cx"> for dep in task.dependencies: </span><span class="lines">@@ -605,6 +620,7 @@ </span><span class="cx"> childtask = trans.get_task_by_mapper(processor.mapper) </span><span class="cx"> # is this dependency involved in one of the cycles ? </span><span class="cx"> cyclicaldep = dep.targettask in cycles and trans.get_task_by_mapper(dep.processor.mapper) in cycles </span><ins>+ </ins><span class="cx"> if isdelete: </span><span class="cx"> childlist = childlist.unchanged_items() + childlist.deleted_items() </span><span class="cx"> else: </span><span class="lines">@@ -614,26 +630,20 @@ </span><span class="cx"> if o is None: </span><span class="cx"> continue </span><span class="cx"> if not o in childtask.objects: </span><del>- # item needs to be saved since its added, or attached to a deleted object - if isdelete: - childtask.append(o, processor.cascade.delete) - if cyclicaldep: - object_to_original_task[o] = task - if not cyclicaldep: - # not cyclical, so we are done with this - continue - # cyclical, so create an ordered pair for the dependency sort - whosdep = dep.whose_dependent_on_who(obj, o) - if whosdep is not None: - tuples.append(whosdep) - # create a UOWDependencyProcessor representing this pair of objects. - # append it to a UOWTask - if whosdep[0] is obj: - get_dependency_task(obj, dep).append(whosdep[0], isdelete=isdelete) </del><ins>+ object_to_original_task[o] = childtask + if cyclicaldep: + # cyclical, so create an ordered pair for the dependency sort + whosdep = dep.whose_dependent_on_who(obj, o) + if whosdep is not None: + tuples.append(whosdep) + # create a UOWDependencyProcessor representing this pair of objects. + # append it to a UOWTask + if whosdep[0] is obj: + get_dependency_task(obj, dep).append(whosdep[0], isdelete=isdelete) + else: + get_dependency_task(obj, dep).append(whosdep[1], isdelete=isdelete) </ins><span class="cx"> else: </span><del>- get_dependency_task(obj, dep).append(whosdep[1], isdelete=isdelete) - else: - get_dependency_task(obj, dep).append(obj, isdelete=isdelete) </del><ins>+ get_dependency_task(obj, dep).append(obj, isdelete=isdelete) </ins><span class="cx"> </span><span class="cx"> head = DependencySorter(tuples, allobjects).sort() </span><span class="cx"> if head is None: </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-22 17:34:07
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1486] zblog/trunk/components: more 0.2 changes...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1486</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-22 12:33:50 -0500 (Mon, 22 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>more 0.2 changes...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#zblogtrunkcomponentsdatamyc">zblog/trunk/components/data.myc</a></li> <li><a href="#zblogtrunklibzblogcontrollerbootstrappy">zblog/trunk/lib/zblog/controller/bootstrap.py</a></li> <li><a href="#zblogtrunklibzblogdatabase__init__py">zblog/trunk/lib/zblog/database/__init__.py</a></li> <li><a href="#zblogtrunklibzblogdatabasemapperspy">zblog/trunk/lib/zblog/database/mappers.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="zblogtrunkcomponentsdatamyc"></a> <div class="modfile"><h4>Modified: zblog/trunk/components/data.myc (1485 => 1486)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/components/data.myc 2006-05-22 17:31:52 UTC (rev 1485) +++ zblog/trunk/components/data.myc 2006-05-22 17:33:50 UTC (rev 1486) </span><span class="lines">@@ -44,7 +44,10 @@ </span><span class="cx"> keyword=False </span><span class="cx"> </%args> </span><span class="cx"> <%init> </span><del>- posts = session().query(Post).select_by(blog_id=blog.id, keyword=keyword) </del><ins>+ if keyword is not False: + posts = session().query(Post).select_by(blog_id=blog.id, keyword=keyword) + else: + posts = session().query(Post).select_by(blog_id=blog.id) </ins><span class="cx"> if not m.has_content(): </span><span class="cx"> return posts </span><span class="cx"> </%init> </span></span></pre></div> <a id="zblogtrunklibzblogcontrollerbootstrappy"></a> <div class="modfile"><h4>Modified: zblog/trunk/lib/zblog/controller/bootstrap.py (1485 => 1486)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/lib/zblog/controller/bootstrap.py 2006-05-22 17:31:52 UTC (rev 1485) +++ zblog/trunk/lib/zblog/controller/bootstrap.py 2006-05-22 17:33:50 UTC (rev 1486) </span><span class="lines">@@ -60,7 +60,7 @@ </span><span class="cx"> dbform.description = desc['description'] </span><span class="cx"> f['dbtype'].value=dbtype </span><span class="cx"> for field in desc['arguments']: </span><del>- if dbtype == 'sqlite' and field[0] == 'host': </del><ins>+ if dbtype == 'sqlite' and field[0] == 'database': </ins><span class="cx"> default = './data/zblog.db' </span><span class="cx"> else: </span><span class="cx"> default = field[2] </span></span></pre></div> <a id="zblogtrunklibzblogdatabase__init__py"></a> <div class="modfile"><h4>Modified: zblog/trunk/lib/zblog/database/__init__.py (1485 => 1486)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/lib/zblog/database/__init__.py 2006-05-22 17:31:52 UTC (rev 1485) +++ zblog/trunk/lib/zblog/database/__init__.py 2006-05-22 17:33:50 UTC (rev 1486) </span><span class="lines">@@ -47,11 +47,10 @@ </span><span class="cx"> import zblog.domain.user as user </span><span class="cx"> import zblog.database.mappers as mapper </span><span class="cx"> mapper.start_session() </span><del>- mapper.begin() </del><span class="cx"> try: </span><span class="cx"> u = user.User(admin_username, 'Administrator', admin_password, user.administrator) </span><span class="cx"> mapper.session().save(u) </span><del>- mapper.commit() </del><ins>+ mapper.session().flush() </ins><span class="cx"> finally: </span><span class="cx"> engine.echo = e </span><span class="cx"> engine.logger = sys.stdout </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="zblogtrunklibzblogdatabasemapperspy"></a> <div class="modfile"><h4>Modified: zblog/trunk/lib/zblog/database/mappers.py (1485 => 1486)</h4> <pre class="diff"><span> <span class="info">--- zblog/trunk/lib/zblog/database/mappers.py 2006-05-22 17:31:52 UTC (rev 1485) +++ zblog/trunk/lib/zblog/database/mappers.py 2006-05-22 17:33:50 UTC (rev 1486) </span><span class="lines">@@ -6,6 +6,7 @@ </span><span class="cx"> from zblog.domain.blog import * </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> import sqlalchemy.util as util </span><ins>+import sys </ins><span class="cx"> </span><span class="cx"> # User mapper. Here, we redefine the names of some of the columns </span><span class="cx"> # to different property names. normally the table columns are all </span><span class="lines">@@ -17,6 +18,9 @@ </span><span class="cx"> 'crypt_password':tables.users.c.password, </span><span class="cx"> }) </span><span class="cx"> </span><ins>+#print [(k,p) for k, p in user.User.__dict__.iteritems()] +#sys.exit(0) + </ins><span class="cx"> # blog mapper. this contains a reference to the user mapper, </span><span class="cx"> # and also installs a "backreference" on that relationship to handle it </span><span class="cx"> # in both ways. this will also attach a 'blogs' property to the user mapper. </span><span class="lines">@@ -54,10 +58,13 @@ </span><span class="cx"> outerjoin(tables.posts, tables.comments) </span><span class="cx"> ], </span><span class="cx"> group_by=[ </span><del>- c for c in tables.posts.c </del><ins>+ c for c in tables.posts.c if c.key != 'body' </ins><span class="cx"> ] </span><span class="cx"> ) .alias('postswcount') </span><span class="cx"> </span><ins>+#print str(posts_with_ccount) +#sys.exit(0) + </ins><span class="cx"> # then create a Post mapper on that query. </span><span class="cx"> # we have the body as "deferred" so that it loads only when needed, </span><span class="cx"> # the user as a Lazy load, since the lazy load will run only once per user and </span><span class="lines">@@ -124,7 +131,7 @@ </span><span class="cx"> </span><span class="cx"> def start_session(): </span><span class="cx"> """creates a new session for the start of a request.""" </span><del>- trans.session = create_session(bind_to=zblog.database.engine) </del><ins>+ trans.session = create_session(bind_to=zblog.database.engine, echo_uow=True) </ins><span class="cx"> </span><span class="cx"> def session(): </span><span class="cx"> return trans.session </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-22 17:32:07
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1485] sqlalchemy/branches/schema/test: making UOW dump slightly more readable (the way a hurricaine is slightly less windy than a tornado)</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1485</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-22 12:31:52 -0500 (Mon, 22 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>making UOW dump slightly more readable (the way a hurricaine is slightly less windy than a tornado)</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestcyclespy">sqlalchemy/branches/schema/test/cycles.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1484 => 1485)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-22 01:14:42 UTC (rev 1484) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-22 17:31:52 UTC (rev 1485) </span><span class="lines">@@ -513,10 +513,10 @@ </span><span class="cx"> """called by a UnitOfWork object to save objects, which involves either an INSERT or </span><span class="cx"> an UPDATE statement for each table used by this mapper, for each element of the </span><span class="cx"> list.""" </span><del>- print "SAVE_OBJ MAPPER", self.class_.__name__, objects </del><ins>+ #print "SAVE_OBJ MAPPER", self.class_.__name__, objects </ins><span class="cx"> connection = uow.transaction.connection(self) </span><span class="cx"> for table in self.tables: </span><del>- print "SAVE_OBJ table ", self.class_.__name__, table.name </del><ins>+ #print "SAVE_OBJ table ", self.class_.__name__, table.name </ins><span class="cx"> # looping through our set of tables, which are all "real" tables, as opposed </span><span class="cx"> # to our main table which might be a select statement or something non-writeable </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1484 => 1485)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-22 01:14:42 UTC (rev 1484) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-22 17:31:52 UTC (rev 1485) </span><span class="lines">@@ -279,7 +279,7 @@ </span><span class="cx"> refreshed/updated to reflect a recent save/upcoming delete operation, but not a full </span><span class="cx"> save/delete operation on the object itself, unless an additional save/delete </span><span class="cx"> registration is entered for the object.""" </span><del>- print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) </del><ins>+ #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) </ins><span class="cx"> # things can get really confusing if theres duplicate instances floating around, </span><span class="cx"> # so make sure everything is OK </span><span class="cx"> self.uow._validate_obj(obj) </span><span class="lines">@@ -413,7 +413,6 @@ </span><span class="cx"> self.listonly = True </span><span class="cx"> self.childtasks = [] </span><span class="cx"> self.isdelete = False </span><del>- self.mapper = None </del><span class="cx"> def __repr__(self): </span><span class="cx"> return "UOWTaskElement/%d: %s/%d %s" % (id(self), self.obj.__class__.__name__, id(self.obj), (self.listonly and 'listonly' or (self.isdelete and 'delete' or 'save')) ) </span><span class="cx"> </span><span class="lines">@@ -451,9 +450,7 @@ </span><span class="cx"> self.dependencies = [] </span><span class="cx"> self.cyclical_dependencies = [] </span><span class="cx"> self.circular = None </span><del>- self.postcircular = None </del><span class="cx"> self.childtasks = [] </span><del>-# print "NEW TASK", repr(self) </del><span class="cx"> </span><span class="cx"> def is_empty(self): </span><span class="cx"> return len(self.objects) == 0 and len(self.dependencies) == 0 and len(self.childtasks) == 0 </span><span class="lines">@@ -543,7 +540,6 @@ </span><span class="cx"> this is not the normal case; this logic only kicks in when something like </span><span class="cx"> a hierarchical tree is being represented. </span><span class="cx"> </span><del>- TODO: refactor heavily </del><span class="cx"> """ </span><span class="cx"> </span><span class="cx"> allobjects = [] </span><span class="lines">@@ -553,7 +549,7 @@ </span><span class="cx"> </span><span class="cx"> cycles = Set(cycles) </span><span class="cx"> </span><del>- print "BEGIN CIRC SORT-------" </del><ins>+ #print "BEGIN CIRC SORT-------" </ins><span class="cx"> # dependency processors that arent part of the cyclical thing </span><span class="cx"> # get put here </span><span class="cx"> extradeplist = [] </span><span class="lines">@@ -630,8 +626,8 @@ </span><span class="cx"> whosdep = dep.whose_dependent_on_who(obj, o) </span><span class="cx"> if whosdep is not None: </span><span class="cx"> tuples.append(whosdep) </span><del>- # then locate a UOWDependencyProcessor to add the object onto, which - # will handle the modifications between saves/deletes </del><ins>+ # create a UOWDependencyProcessor representing this pair of objects. + # append it to a UOWTask </ins><span class="cx"> if whosdep[0] is obj: </span><span class="cx"> get_dependency_task(obj, dep).append(whosdep[0], isdelete=isdelete) </span><span class="cx"> else: </span><span class="lines">@@ -643,7 +639,7 @@ </span><span class="cx"> if head is None: </span><span class="cx"> return None </span><span class="cx"> </span><del>- print str(head) </del><ins>+ #print str(head) </ins><span class="cx"> </span><span class="cx"> hierarchical_tasks = {} </span><span class="cx"> def get_object_task(obj): </span><span class="lines">@@ -655,8 +651,12 @@ </span><span class="cx"> </span><span class="cx"> def make_task_tree(node, parenttask): </span><span class="cx"> """takes a dependency-sorted tree of objects and creates a tree of UOWTasks""" </span><del>- print "MAKETASKTREE", node.item </del><ins>+ #print "MAKETASKTREE", node.item + </ins><span class="cx"> t = get_object_task(node.item) </span><ins>+ for n in node.children: + t2 = make_task_tree(n, t) + </ins><span class="cx"> can_add_to_parent = t.mapper is parenttask.mapper </span><span class="cx"> original_task = object_to_original_task[node.item] </span><span class="cx"> if original_task.objects.has_key(node.item): </span><span class="lines">@@ -666,15 +666,13 @@ </span><span class="cx"> t.append(node.item, original_task.objects[node.item].listonly, isdelete=original_task.objects[node.item].isdelete) </span><span class="cx"> parenttask.append(None, listonly=False, isdelete=original_task.objects[node.item].isdelete, childtask=t) </span><span class="cx"> else: </span><del>- parenttask.append(None, childtask=t) </del><ins>+ parenttask.append(None, listonly=False, isdelete=original_task.objects[node.item].isdelete, childtask=t) </ins><span class="cx"> if dependencies.has_key(node.item): </span><span class="cx"> for depprocessor, deptask in dependencies[node.item].iteritems(): </span><span class="cx"> if can_add_to_parent: </span><span class="cx"> parenttask.cyclical_dependencies.append(depprocessor.branch(deptask)) </span><span class="cx"> else: </span><span class="cx"> t.cyclical_dependencies.append(depprocessor.branch(deptask)) </span><del>- for n in node.children: - t2 = make_task_tree(n, t) </del><span class="cx"> return t </span><span class="cx"> </span><span class="cx"> # this is the new "circular" UOWTask which will execute in place of "self" </span><span class="lines">@@ -685,8 +683,7 @@ </span><span class="cx"> t.dependencies += [d for d in extradeplist] </span><span class="cx"> t.childtasks = self.childtasks </span><span class="cx"> make_task_tree(head, t) </span><del>- print [o.obj for o in t.objects.values()] - print t.dump() </del><ins>+ #print t.dump() </ins><span class="cx"> return t </span><span class="cx"> </span><span class="cx"> def dump(self): </span><span class="lines">@@ -715,38 +712,39 @@ </span><span class="cx"> else: </span><span class="cx"> val = [t for t in proc.targettask.objects.values() if not t.isdelete] </span><span class="cx"> </span><del>- buf.write(_indent() + " |- UOWDependencyProcessor(%d) %s attribute on %s (%s)\n" % ( - id(proc), </del><ins>+ buf.write(_indent() + " |- %s attribute on %s (UOWDependencyProcessor(%d) processing %s)\n" % ( </ins><span class="cx"> repr(proc.processor.key), </span><span class="cx"> (proc.isdeletefrom and </span><span class="cx"> "%s's to be deleted" % _repr_task_class(proc.targettask) </span><span class="cx"> or "saved %s's" % _repr_task_class(proc.targettask)), </span><ins>+ id(proc), </ins><span class="cx"> _repr_task(proc.targettask)) </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> if len(val) == 0: </span><span class="cx"> buf.write(_indent() + " | |-" + "(no objects)\n") </span><span class="cx"> for v in val: </span><del>- buf.write(_indent() + " | |-" + _repr_task_element(v) + "\n") </del><ins>+ buf.write(_indent() + " | |-" + _repr_task_element(v, proc.processor.key) + "\n") </ins><span class="cx"> </span><del>- def _repr_task_element(te): </del><ins>+ def _repr_task_element(te, attribute=None): </ins><span class="cx"> if te.obj is None: </span><span class="cx"> objid = "(placeholder)" </span><span class="cx"> else: </span><del>- objid = "%s(%d)" % (te.obj.__class__.__name__, id(te.obj)) - return "UOWTaskElement(%d): %s %s%s" % (id(te), objid, (te.listonly and '(listonly)' or (te.isdelete and '(delete' or '(save')), - (te.mapper is not None and " w/ " + str(te.mapper) + ")" or ")") - ) </del><ins>+ if attribute is not None: + objid = "%s(%d).%s" % (te.obj.__class__.__name__, id(te.obj), attribute) + else: + objid = "%s(%d)" % (te.obj.__class__.__name__, id(te.obj)) + return "%s (UOWTaskElement(%d, %s))" % (objid, id(te), (te.listonly and 'listonly' or (te.isdelete and 'delete' or 'save'))) </ins><span class="cx"> </span><span class="cx"> def _repr_task(task): </span><span class="cx"> if task.mapper is not None: </span><span class="cx"> if task.mapper.__class__.__name__ == 'Mapper': </span><del>- name = task.mapper.class_.__name__ + "/" + str(task.mapper.local_table) + "/" + str(id(task.mapper)) </del><ins>+ name = task.mapper.class_.__name__ + "/" + task.mapper.local_table.name + "/" + str(task.mapper.entity_name) </ins><span class="cx"> else: </span><span class="cx"> name = repr(task.mapper) </span><span class="cx"> else: </span><span class="cx"> name = '(none)' </span><del>- return ("UOWTask(%d) '%s'" % (id(task), name)) </del><ins>+ return ("UOWTask(%d, %s)" % (id(task), name)) </ins><span class="cx"> def _repr_task_class(task): </span><span class="cx"> if task.mapper is not None and task.mapper.__class__.__name__ == 'Mapper': </span><span class="cx"> return task.mapper.class_.__name__ </span><span class="lines">@@ -774,7 +772,7 @@ </span><span class="cx"> if rec.listonly: </span><span class="cx"> continue </span><span class="cx"> header(buf, _indent() + " |- Save elements\n") </span><del>- buf.write(_indent() + " |- Save: " + _repr_task_element(rec) + "\n") </del><ins>+ buf.write(_indent() + " |- " + _repr_task_element(rec) + "\n") </ins><span class="cx"> for dep in self.cyclical_save_dependencies(): </span><span class="cx"> header(buf, _indent() + " |- Cyclical Save dependencies\n") </span><span class="cx"> _dump_processor(dep) </span><span class="lines">@@ -806,9 +804,13 @@ </span><span class="cx"> if rec.listonly: </span><span class="cx"> continue </span><span class="cx"> header(buf, _indent() + " |- Delete elements\n") </span><del>- buf.write(_indent() + " |- Delete: " + _repr_task_element(rec) + "\n") </del><ins>+ buf.write(_indent() + " |- " + _repr_task_element(rec) + "\n") </ins><span class="cx"> </span><del>- buf.write(_indent() + " |----\n") </del><ins>+ if self.is_empty(): + buf.write(_indent() + " |- (empty task)\n") + else: + buf.write(_indent() + " |----\n") + </ins><span class="cx"> buf.write(_indent() + "\n") </span><span class="cx"> </span><span class="cx"> def __repr__(self): </span></span></pre></div> <a id="sqlalchemybranchesschematestcyclespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/cycles.py (1484 => 1485)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/cycles.py 2006-05-22 01:14:42 UTC (rev 1484) +++ sqlalchemy/branches/schema/test/cycles.py 2006-05-22 17:31:52 UTC (rev 1485) </span><span class="lines">@@ -49,7 +49,7 @@ </span><span class="cx"> }) </span><span class="cx"> a = C1('head c1') </span><span class="cx"> a.c1s.append(C1('another c1')) </span><del>- sess = create_session() </del><ins>+ sess = create_session(echo_uow=True) </ins><span class="cx"> sess.save(a) </span><span class="cx"> sess.flush() </span><span class="cx"> sess.delete(a) </span><span class="lines">@@ -73,7 +73,7 @@ </span><span class="cx"> a.c1s[0].c1s.append(C1('subchild2')) </span><span class="cx"> a.c1s[1].c2s.append(C2('child2 data1')) </span><span class="cx"> a.c1s[1].c2s.append(C2('child2 data2')) </span><del>- sess = create_session() </del><ins>+ sess = create_session(echo_uow=True) </ins><span class="cx"> sess.save(a) </span><span class="cx"> sess.flush() </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-22 01:14:57
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1484] sqlalchemy/branches/schema/test: fixes to circular dependency sort, unittests for pg...tweaks for getting zblog going</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1484</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 20:14:42 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixes to circular dependency sort, unittests for pg...tweaks for getting zblog going</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextactivemapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormunitofworkpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py</a></li> <li><a href="#sqlalchemybranchesschematestactivemapperpy">sqlalchemy/branches/schema/test/activemapper.py</a></li> <li><a href="#sqlalchemybranchesschematestindexespy">sqlalchemy/branches/schema/test/indexes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -274,7 +274,7 @@ </span><span class="cx"> return self.module </span><span class="cx"> </span><span class="cx"> def has_table(self, connection, table_name): </span><del>- cursor = connection.execute("""select relname from pg_class where relname = %(name)s""", {'name':table_name}) </del><ins>+ cursor = connection.execute("""select relname from pg_class where lower(relname) = %(name)s""", {'name':table_name.lower()}) </ins><span class="cx"> return bool( not not cursor.rowcount ) </span><span class="cx"> </span><span class="cx"> def reflecttable(self, connection, table): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -177,4 +177,7 @@ </span><span class="cx"> def create_tables(): </span><span class="cx"> for metadata in ActiveMapperMeta.metadatas: </span><span class="cx"> metadata.create_all() </span><ins>+def drop_tables(): + for metadata in ActiveMapperMeta.metadatas: + metadata.drop_all() </ins><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -513,10 +513,10 @@ </span><span class="cx"> """called by a UnitOfWork object to save objects, which involves either an INSERT or </span><span class="cx"> an UPDATE statement for each table used by this mapper, for each element of the </span><span class="cx"> list.""" </span><del>- #print "SAVE_OBJ MAPPER", self.class_.__name__, objects </del><ins>+ print "SAVE_OBJ MAPPER", self.class_.__name__, objects </ins><span class="cx"> connection = uow.transaction.connection(self) </span><span class="cx"> for table in self.tables: </span><del>- #print "SAVE_OBJ table ", self.class_.__name__, table.name </del><ins>+ print "SAVE_OBJ table ", self.class_.__name__, table.name </ins><span class="cx"> # looping through our set of tables, which are all "real" tables, as opposed </span><span class="cx"> # to our main table which might be a select statement or something non-writeable </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -347,7 +347,12 @@ </span><span class="cx"> self._register_dirty(object) </span><span class="cx"> else: </span><span class="cx"> self._register_clean(object) </span><del>- </del><ins>+ + def _register_changed(self, obj): + if hasattr(obj, '_instance_key'): + self._register_dirty(obj) + else: + self._register_new(obj) </ins><span class="cx"> def _register_new(self, obj): </span><span class="cx"> self._attach(obj) </span><span class="cx"> self.uow.register_new(obj) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/unitofwork.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -45,7 +45,7 @@ </span><span class="cx"> def do_value_changed(self, obj, key, item, listval, isdelete): </span><span class="cx"> sess = object_session(obj) </span><span class="cx"> if sess is not None: </span><del>- sess._register_dirty(obj) </del><ins>+ sess._register_changed(obj) </ins><span class="cx"> if self.cascade is not None: </span><span class="cx"> if not isdelete: </span><span class="cx"> if self.cascade.save_update: </span><span class="lines">@@ -64,7 +64,7 @@ </span><span class="cx"> obj = self.obj </span><span class="cx"> sess = object_session(obj) </span><span class="cx"> if sess is not None: </span><del>- sess._register_dirty(obj) </del><ins>+ sess._register_changed(obj) </ins><span class="cx"> if newvalue is not None and self.cascade is not None: </span><span class="cx"> if self.cascade.save_update: </span><span class="cx"> sess.save_or_update(newvalue) </span><span class="lines">@@ -279,7 +279,7 @@ </span><span class="cx"> refreshed/updated to reflect a recent save/upcoming delete operation, but not a full </span><span class="cx"> save/delete operation on the object itself, unless an additional save/delete </span><span class="cx"> registration is entered for the object.""" </span><del>- #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) </del><ins>+ print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) </ins><span class="cx"> # things can get really confusing if theres duplicate instances floating around, </span><span class="cx"> # so make sure everything is OK </span><span class="cx"> self.uow._validate_obj(obj) </span><span class="lines">@@ -542,9 +542,8 @@ </span><span class="cx"> </span><span class="cx"> this is not the normal case; this logic only kicks in when something like </span><span class="cx"> a hierarchical tree is being represented. </span><del>- - TODO: dont understand this code ? well neither do I ! it takes me - an hour to re-understand this code completely, which is definitely an issue. </del><ins>+ + TODO: refactor heavily </ins><span class="cx"> """ </span><span class="cx"> </span><span class="cx"> allobjects = [] </span><span class="lines">@@ -552,24 +551,15 @@ </span><span class="cx"> allobjects += task.objects.keys() </span><span class="cx"> tuples = [] </span><span class="cx"> </span><del>- objecttotask = {} - </del><span class="cx"> cycles = Set(cycles) </span><span class="cx"> </span><ins>+ print "BEGIN CIRC SORT-------" </ins><span class="cx"> # dependency processors that arent part of the cyclical thing </span><span class="cx"> # get put here </span><span class="cx"> extradeplist = [] </span><span class="cx"> </span><del>- # this creates a map of UOWTasks mapped to individual objects. - def get_object_task(parent, obj): - try: - return objecttotask[obj] - except KeyError: - t = UOWTask(None, parent.mapper) - t.parent = parent - objecttotask[obj] = t - return t - </del><ins>+ object_to_original_task = {} + </ins><span class="cx"> # this creates a map of UOWTasks mapped to a particular object </span><span class="cx"> # and a particular dependency processor. </span><span class="cx"> dependencies = {} </span><span class="lines">@@ -588,8 +578,7 @@ </span><span class="cx"> </span><span class="cx"> # work out a list of all the "dependency processors" that </span><span class="cx"> # represent objects that have to be dependency sorted at the </span><del>- # per-object level. all other dependency processors go in - # "extradep." </del><ins>+ # per-object level. </ins><span class="cx"> deps_by_targettask = {} </span><span class="cx"> for task in cycles: </span><span class="cx"> for dep in task.dependencies: </span><span class="lines">@@ -601,11 +590,9 @@ </span><span class="cx"> for task in cycles: </span><span class="cx"> for taskelement in task.objects.values(): </span><span class="cx"> obj = taskelement.obj </span><ins>+ object_to_original_task[obj] = task </ins><span class="cx"> #print "OBJ", repr(obj), "TASK", repr(task) </span><span class="cx"> </span><del>- # create a placeholder UOWTask that may be built into the final - # task tree - get_object_task(task, obj) </del><span class="cx"> for dep in deps_by_targettask.get(task, []): </span><span class="cx"> (processor, targettask, isdelete) = (dep.processor, dep.targettask, dep.isdeletefrom) </span><span class="cx"> if taskelement.isdelete is not dep.isdeletefrom: </span><span class="lines">@@ -635,9 +622,7 @@ </span><span class="cx"> if isdelete: </span><span class="cx"> childtask.append(o, processor.cascade.delete) </span><span class="cx"> if cyclicaldep: </span><del>- # cyclical, so create a placeholder UOWTask that may be built into the - # final task tree - t = get_object_task(childtask, o) </del><ins>+ object_to_original_task[o] = task </ins><span class="cx"> if not cyclicaldep: </span><span class="cx"> # not cyclical, so we are done with this </span><span class="cx"> continue </span><span class="lines">@@ -648,9 +633,9 @@ </span><span class="cx"> # then locate a UOWDependencyProcessor to add the object onto, which </span><span class="cx"> # will handle the modifications between saves/deletes </span><span class="cx"> if whosdep[0] is obj: </span><del>- get_dependency_task(whosdep[0], dep).append(whosdep[0], isdelete=isdelete) </del><ins>+ get_dependency_task(obj, dep).append(whosdep[0], isdelete=isdelete) </ins><span class="cx"> else: </span><del>- get_dependency_task(whosdep[0], dep).append(whosdep[1], isdelete=isdelete) </del><ins>+ get_dependency_task(obj, dep).append(whosdep[1], isdelete=isdelete) </ins><span class="cx"> else: </span><span class="cx"> get_dependency_task(obj, dep).append(obj, isdelete=isdelete) </span><span class="cx"> </span><span class="lines">@@ -658,18 +643,30 @@ </span><span class="cx"> if head is None: </span><span class="cx"> return None </span><span class="cx"> </span><del>- #print str(head) </del><ins>+ print str(head) </ins><span class="cx"> </span><ins>+ hierarchical_tasks = {} + def get_object_task(obj): + try: + return hierarchical_tasks[obj] + except KeyError: + originating_task = object_to_original_task[obj] + return hierarchical_tasks.setdefault(obj, UOWTask(None, originating_task.mapper)) + </ins><span class="cx"> def make_task_tree(node, parenttask): </span><span class="cx"> """takes a dependency-sorted tree of objects and creates a tree of UOWTasks""" </span><del>- t = objecttotask[node.item] </del><ins>+ print "MAKETASKTREE", node.item + t = get_object_task(node.item) </ins><span class="cx"> can_add_to_parent = t.mapper is parenttask.mapper </span><del>- if t.parent.objects.has_key(node.item): </del><ins>+ original_task = object_to_original_task[node.item] + if original_task.objects.has_key(node.item): </ins><span class="cx"> if can_add_to_parent: </span><del>- parenttask.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete, childtask=t) </del><ins>+ parenttask.append(node.item, original_task.objects[node.item].listonly, isdelete=original_task.objects[node.item].isdelete, childtask=t) </ins><span class="cx"> else: </span><del>- t.append(node.item, t.parent.objects[node.item].listonly, isdelete=t.parent.objects[node.item].isdelete) - parenttask.append(None, listonly=False, isdelete=t.parent.objects[node.item].isdelete, childtask=t) </del><ins>+ t.append(node.item, original_task.objects[node.item].listonly, isdelete=original_task.objects[node.item].isdelete) + parenttask.append(None, listonly=False, isdelete=original_task.objects[node.item].isdelete, childtask=t) + else: + parenttask.append(None, childtask=t) </ins><span class="cx"> if dependencies.has_key(node.item): </span><span class="cx"> for depprocessor, deptask in dependencies[node.item].iteritems(): </span><span class="cx"> if can_add_to_parent: </span><span class="lines">@@ -688,6 +685,8 @@ </span><span class="cx"> t.dependencies += [d for d in extradeplist] </span><span class="cx"> t.childtasks = self.childtasks </span><span class="cx"> make_task_tree(head, t) </span><ins>+ print [o.obj for o in t.objects.values()] + print t.dump() </ins><span class="cx"> return t </span><span class="cx"> </span><span class="cx"> def dump(self): </span></span></pre></div> <a id="sqlalchemybranchesschematestactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/activemapper.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/activemapper.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/test/activemapper.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -63,6 +63,7 @@ </span><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><span class="cx"> clear_mappers() </span><ins>+ activemapper.drop_tables() </ins><span class="cx"> </span><span class="cx"> def tearDown(self): </span><span class="cx"> for t in activemapper.metadata.table_iterator(reverse=True): </span></span></pre></div> <a id="sqlalchemybranchesschematestindexespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/indexes.py (1483 => 1484)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/indexes.py 2006-05-21 22:36:53 UTC (rev 1483) +++ sqlalchemy/branches/schema/test/indexes.py 2006-05-22 01:14:42 UTC (rev 1484) </span><span class="lines">@@ -5,59 +5,51 @@ </span><span class="cx"> class IndexTest(testbase.AssertMixin): </span><span class="cx"> </span><span class="cx"> def setUp(self): </span><del>- self.created = [] </del><ins>+ global metadata + metadata = BoundMetaData(testbase.db) </ins><span class="cx"> self.echo = testbase.db.echo </span><span class="cx"> self.logger = testbase.db.logger </span><span class="cx"> </span><span class="cx"> def tearDown(self): </span><span class="cx"> testbase.db.echo = self.echo </span><span class="cx"> testbase.db.logger = testbase.db.engine.logger = self.logger </span><del>- if self.created: - self.created.reverse() - for entity in self.created: - entity.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> </span><span class="cx"> def test_index_create(self): </span><del>- employees = Table('employees', testbase.metadata, </del><ins>+ employees = Table('employees', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> Column('first_name', String(30)), </span><span class="cx"> Column('last_name', String(30)), </span><span class="cx"> Column('email_address', String(30))) </span><span class="cx"> employees.create() </span><del>- self.created.append(employees) </del><span class="cx"> </span><span class="cx"> i = Index('employee_name_index', </span><span class="cx"> employees.c.last_name, employees.c.first_name) </span><span class="cx"> i.create() </span><del>- self.created.append(i) </del><span class="cx"> assert employees.indexes['employee_name_index'] is i </span><span class="cx"> </span><span class="cx"> i2 = Index('employee_email_index', </span><span class="cx"> employees.c.email_address, unique=True) </span><span class="cx"> i2.create() </span><del>- self.created.append(i2) </del><span class="cx"> assert employees.indexes['employee_email_index'] is i2 </span><span class="cx"> </span><span class="cx"> def test_index_create_camelcase(self): </span><span class="cx"> """test that mixed-case index identifiers are legal""" </span><del>- employees = Table('companyEmployees', testbase.metadata, </del><ins>+ employees = Table('companyEmployees', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> Column('firstName', String(30)), </span><span class="cx"> Column('lastName', String(30)), </span><span class="cx"> Column('emailAddress', String(30))) </span><span class="cx"> </span><span class="cx"> employees.create() </span><del>- self.created.append(employees) </del><span class="cx"> </span><span class="cx"> i = Index('employeeNameIndex', </span><span class="cx"> employees.c.lastName, employees.c.firstName) </span><span class="cx"> i.create() </span><del>- self.created.append(i) </del><span class="cx"> </span><span class="cx"> i = Index('employeeEmailIndex', </span><span class="cx"> employees.c.emailAddress, unique=True) </span><span class="cx"> i.create() </span><del>- self.created.append(i) </del><span class="cx"> </span><span class="cx"> # Check that the table is useable. This is mostly for pg, </span><span class="cx"> # which can be somewhat sticky with mixed-case identifiers </span><span class="lines">@@ -76,7 +68,7 @@ </span><span class="cx"> stream.write = capt.append </span><span class="cx"> testbase.db.logger = testbase.db.engine.logger = stream </span><span class="cx"> </span><del>- events = Table('events', testbase.metadata, </del><ins>+ events = Table('events', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> Column('name', String(30), unique=True), </span><span class="cx"> Column('location', String(30), index=True), </span><span class="lines">@@ -94,7 +86,6 @@ </span><span class="cx"> assert len(index_names) == 4 </span><span class="cx"> </span><span class="cx"> events.create() </span><del>- self.created.append(events) </del><span class="cx"> </span><span class="cx"> # verify that the table is functional </span><span class="cx"> events.insert().execute(id=1, name='hockey finals', location='rink', </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-21 22:37:11
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1483] sqlalchemy/branches/schema/lib/sqlalchemy: fixes...getting mapper column targeting to be more liberal, add_property fix</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1483</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 17:36:53 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixes...getting mapper column targeting to be more liberal, add_property fix</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1482 => 1483)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-21 21:17:58 UTC (rev 1482) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-21 22:36:53 UTC (rev 1483) </span><span class="lines">@@ -522,5 +522,8 @@ </span><span class="cx"> if not hasattr(class_, '_attribute_manager'): </span><span class="cx"> class_._attribute_manager = self </span><span class="cx"> typecallable = getattr(class_, key, None) </span><ins>+ # TODO: look at existing properties on the class, and adapt them to the SmartProperty + if isinstance(typecallable, SmartProperty): + typecallable = None </ins><span class="cx"> setattr(class_, key, self.create_prop(class_, key, uselist, callable_, typecallable=typecallable, **kwargs)) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py (1482 => 1483)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-05-21 21:17:58 UTC (rev 1482) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/sqlite.py 2006-05-21 22:36:53 UTC (rev 1483) </span><span class="lines">@@ -116,7 +116,7 @@ </span><span class="cx"> return {'name':'sqlite', </span><span class="cx"> 'description':'SQLite', </span><span class="cx"> 'arguments':[ </span><del>- ('host', "Database Filename",None) </del><ins>+ ('database', "Database Filename",None) </ins><span class="cx"> ]} </span><span class="cx"> </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1482 => 1483)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 21:17:58 UTC (rev 1482) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 22:36:53 UTC (rev 1483) </span><span class="lines">@@ -1084,21 +1084,22 @@ </span><span class="cx"> def __init__(self, selectable): </span><span class="cx"> super(TranslatingDict, self).__init__() </span><span class="cx"> self.selectable = selectable </span><ins>+ def __translate_col(self, col): + ourcol = self.selectable.corresponding_column(col, keys_ok=False, raiseerr=False) + if ourcol is None: + return col + else: + return ourcol </ins><span class="cx"> def __getitem__(self, col): </span><del>- ourcol = self.selectable.corresponding_column(col) - return super(TranslatingDict, self).__getitem__(ourcol) </del><ins>+ return super(TranslatingDict, self).__getitem__(self.__translate_col(col)) </ins><span class="cx"> def has_key(self, col): </span><del>- ourcol = self.selectable.corresponding_column(col) - return super(TranslatingDict, self).has_key(ourcol) </del><ins>+ return super(TranslatingDict, self).has_key(self.__translate_col(col)) </ins><span class="cx"> def __setitem__(self, col, value): </span><del>- ourcol = self.selectable.corresponding_column(col) - return super(TranslatingDict, self).__setitem__(ourcol, value) </del><ins>+ return super(TranslatingDict, self).__setitem__(self.__translate_col(col), value) </ins><span class="cx"> def __contains__(self, col): </span><del>- ourcol = self.selectable.corresponding_column(col) - return super(TranslatingDict, self).__contains__(ourcol) </del><ins>+ return self.has_key(col) </ins><span class="cx"> def setdefault(self, col, value): </span><del>- ourcol = self.selectable.corresponding_column(col) - return super(TranslatingDict, self).setdefault(ourcol, value) </del><ins>+ return super(TranslatingDict, self).setdefault(self.__translate_col(col), value) </ins><span class="cx"> </span><span class="cx"> class ClassKey(object): </span><span class="cx"> """keys a class and an entity name to a mapper, via the mapper_registry""" </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/util.py (1482 => 1483)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-05-21 21:17:58 UTC (rev 1482) +++ sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-05-21 22:36:53 UTC (rev 1483) </span><span class="lines">@@ -255,9 +255,9 @@ </span><span class="cx"> """sets the data for this HistoryArraySet to be that of the given data. </span><span class="cx"> duplicates in the incoming list will be removed.""" </span><span class="cx"> # first mark everything current as "deleted" </span><del>- for i in self.data: - self.records[i] = False - self.do_value_deleted(value) </del><ins>+ for item in self.data: + self.records[item] = False + self.do_value_deleted(item) </ins><span class="cx"> </span><span class="cx"> # switch array </span><span class="cx"> self.data = data </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-21 21:18:15
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1482] sqlalchemy/branches/schema/test: activemapper migrated to 0.2, unit test cleaned up, all unit tests working for postgres/sqlite/mysql,</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1482</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 16:17:58 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>activemapper migrated to 0.2, unit test cleaned up, all unit tests working for postgres/sqlite/mysql, broke out "assignmapper" into its own mod, some games with mapper extensions piling up, factoring change to HistoryArraySet so that update operations that hit backref extensions in the attribute package dont collide with the HAS in an invalid state, various other fixes</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt">sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextactivemapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormtopologicalpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/topological.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemybranchesschematestactivemapperpy">sqlalchemy/branches/schema/test/activemapper.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> <li><a href="#sqlalchemybranchesschematestcyclespy">sqlalchemy/branches/schema/test/cycles.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschematesttestbasepy">sqlalchemy/branches/schema/test/testbase.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextassignmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/assignmapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -9,11 +9,11 @@ </span><span class="cx"> </span><span class="cx"> The examples below all include a dump of the generated SQL corresponding to the query object, as well as a dump of the statement's bind parameters. In all cases, bind parameters are shown as named parameters using the colon format (i.e. ':name'). When the statement is compiled into a database-specific version, the named-parameter statement and its bind values are converted to the proper paramstyle for that database automatically. </span><span class="cx"> </span><del>-For this section, we will mostly use the implcit style of execution, meaning the `Table` objects are associated with an instance of `BoundMetaData`, and constructed `ClauseElement` objects support self-execution. We will also assume the following configuration: </del><ins>+For this section, we will mostly use the implcit style of execution, meaning the `Table` objects are associated with an instance of `BoundMetaData`, and constructed `ClauseElement` objects support self-execution. Assume the following configuration: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> from sqlalchemy import * </span><del>- metadata = BoundMetaData('sqlite://filename=mydb', strategy='threadlocal', echo=True) </del><ins>+ metadata = BoundMetaData('sqlite:///mydb.db', strategy='threadlocal', echo=True) </ins><span class="cx"> </span><span class="cx"> # a table to store users </span><span class="cx"> users = Table('users', metadata, </span><span class="lines">@@ -87,13 +87,13 @@ </span><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # select a literal </span><del>- select(["current_time"], engine=myengine).execute() - {sql}SELECT current_time </del><ins>+ {sql}select(["current_time"], engine=myengine).execute() + SELECT current_time </ins><span class="cx"> {} </span><del>- </del><ins>+ </ins><span class="cx"> # select a function </span><del>- select([func.now()], engine=db).execute() - {sql}SELECT now() </del><ins>+ {sql}select([func.now()], engine=db).execute() + SELECT now() </ins><span class="cx"> {} </span><span class="cx"> </span><span class="cx"> #### Getting Results {@name=resultproxy} </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -208,24 +208,18 @@ </span><span class="cx"> self.set_data(value) </span><span class="cx"> def delattr(self, value, **kwargs): </span><span class="cx"> pass </span><del>- def _setrecord(self, item): - res = util.HistoryArraySet._setrecord(self, item) - if res: - if self.trackparent: - self.sethasparent(item, True) - self.value_changed(self.obj, self.key, item, self, False) - if self.extension is not None: - self.extension.append(self.obj, item) - return res - def _delrecord(self, item): - res = util.HistoryArraySet._delrecord(self, item) - if res: - if self.trackparent: - self.sethasparent(item, False) - self.value_changed(self.obj, self.key, item, self, True) - if self.extension is not None: - self.extension.delete(self.obj, item) - return res </del><ins>+ def do_value_appended(self, item): + if self.trackparent: + self.sethasparent(item, True) + self.value_changed(self.obj, self.key, item, self, False) + if self.extension is not None: + self.extension.append(self.obj, item) + def do_value_deleted(self, item): + if self.trackparent: + self.sethasparent(item, False) + self.value_changed(self.obj, self.key, item, self, True) + if self.extension is not None: + self.extension.delete(self.obj, item) </ins><span class="cx"> </span><span class="cx"> class TriggeredAttribute(ManagedAttribute): </span><span class="cx"> """Used by AttributeManager to allow the attaching of a callable item, representing the future value </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/activemapper.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -1,18 +1,31 @@ </span><del>-from sqlalchemy import objectstore, create_engine, assign_mapper, relation, mapper, join </del><ins>+from sqlalchemy import create_session, relation, mapper, join, DynamicMetaData, class_mapper </ins><span class="cx"> from sqlalchemy import and_, or_ </span><span class="cx"> from sqlalchemy import Table, Column, ForeignKey </span><del>-from sqlalchemy.ext.proxy import ProxyEngine </del><ins>+from sqlalchemy.ext.sessioncontext import SessionContext +from sqlalchemy.ext.assignmapper import assign_mapper +from sqlalchemy import backref as create_backref </ins><span class="cx"> </span><span class="cx"> import inspect </span><span class="cx"> import sys </span><ins>+import sets </ins><span class="cx"> </span><span class="cx"> # </span><span class="cx"> # the "proxy" to the database engine... this can be swapped out at runtime </span><span class="cx"> # </span><del>-engine = ProxyEngine() </del><ins>+metadata = DynamicMetaData("activemapper") </ins><span class="cx"> </span><ins>+# +# thread local SessionContext +# +class Objectstore(SessionContext): + def __getattr__(self, key): + return getattr(self.current, key) + def get_session(self): + return self.current </ins><span class="cx"> </span><ins>+objectstore = Objectstore(create_session) </ins><span class="cx"> </span><ins>+ </ins><span class="cx"> # </span><span class="cx"> # declarative column declaration - this is so that we can infer the colname </span><span class="cx"> # </span><span class="lines">@@ -49,7 +62,7 @@ </span><span class="cx"> </span><span class="cx"> class one_to_one(relationship): </span><span class="cx"> def __init__(self, classname, colname=None, backref=None, private=False, lazy=True): </span><del>- relationship.__init__(self, classname, colname, backref, private, lazy, uselist=False) </del><ins>+ relationship.__init__(self, classname, colname, create_backref(backref, uselist=False), private, lazy, uselist=False) </ins><span class="cx"> </span><span class="cx"> class many_to_many(relationship): </span><span class="cx"> def __init__(self, classname, secondary, backref=None, lazy=True): </span><span class="lines">@@ -82,8 +95,9 @@ </span><span class="cx"> private=reldesc.private, </span><span class="cx"> lazy=reldesc.lazy, </span><span class="cx"> uselist=reldesc.uselist) </span><del>- assign_mapper(klass, klass.table, properties=relations, - inherits=getattr(klass, "_base_mapper", None)) </del><ins>+ class_mapper(klass).add_properties(relations) + #assign_mapper(objectstore, klass, klass.table, properties=relations, + # inherits=getattr(klass, "_base_mapper", None)) </ins><span class="cx"> if was_deferred: __deferred_classes__.remove(klass) </span><span class="cx"> </span><span class="cx"> if not was_deferred: </span><span class="lines">@@ -94,12 +108,12 @@ </span><span class="cx"> </span><span class="cx"> class ActiveMapperMeta(type): </span><span class="cx"> classes = {} </span><del>- </del><ins>+ metadatas = sets.Set() </ins><span class="cx"> def __init__(cls, clsname, bases, dict): </span><span class="cx"> table_name = clsname.lower() </span><span class="cx"> columns = [] </span><span class="cx"> relations = {} </span><del>- _engine = getattr( sys.modules[cls.__module__], "__engine__", engine ) </del><ins>+ _metadata = getattr( sys.modules[cls.__module__], "__metadata__", metadata ) </ins><span class="cx"> </span><span class="cx"> if 'mapping' in dict: </span><span class="cx"> members = inspect.getmembers(dict.get('mapping')) </span><span class="lines">@@ -108,8 +122,8 @@ </span><span class="cx"> table_name = value </span><span class="cx"> continue </span><span class="cx"> </span><del>- if '__engine__' == name: - _engine= value </del><ins>+ if '__metadata__' == name: + _metadata= value </ins><span class="cx"> continue </span><span class="cx"> </span><span class="cx"> if name.startswith('__'): continue </span><span class="lines">@@ -131,14 +145,15 @@ </span><span class="cx"> </span><span class="cx"> if isinstance(value, relationship): </span><span class="cx"> relations[name] = value </span><del>- assert _engine is not None, "No engine specified" - cls.table = Table(table_name, _engine, *columns) </del><ins>+ assert _metadata is not None, "No MetaData specified" + ActiveMapperMeta.metadatas.add(_metadata) + cls.table = Table(table_name, _metadata, *columns) </ins><span class="cx"> # check for inheritence </span><span class="cx"> if hasattr( bases[0], "mapping" ): </span><span class="cx"> cls._base_mapper= bases[0].mapper </span><del>- assign_mapper(cls, cls.table, inherits=cls._base_mapper) </del><ins>+ assign_mapper(objectstore, cls, cls.table, inherits=cls._base_mapper) </ins><span class="cx"> else: </span><del>- assign_mapper(cls, cls.table) </del><ins>+ assign_mapper(objectstore, cls, cls.table) </ins><span class="cx"> cls.relations = relations </span><span class="cx"> ActiveMapperMeta.classes[clsname] = cls </span><span class="cx"> </span><span class="lines">@@ -160,6 +175,6 @@ </span><span class="cx"> # </span><span class="cx"> </span><span class="cx"> def create_tables(): </span><del>- for klass in ActiveMapperMeta.classes.values(): - klass.table.create() </del><ins>+ for metadata in ActiveMapperMeta.metadatas: + metadata.create_all() </ins><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextassignmapperpy"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/lib/sqlalchemy/ext/assignmapper.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/assignmapper.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/assignmapper.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -0,0 +1,34 @@ </span><ins>+from sqlalchemy import mapper, util +import types + +def monkeypatch_query_method(ctx, class_, name): + def do(self, *args, **kwargs): + query = class_.mapper.query(session=ctx.current) + return getattr(query, name)(*args, **kwargs) + setattr(class_, name, classmethod(do)) + +def monkeypatch_objectstore_method(ctx, class_, name): + def do(self, *args, **kwargs): + session = ctx.current + return getattr(session, name)(self, *args, **kwargs) + setattr(class_, name, do) + +def assign_mapper(ctx, class_, *args, **kwargs): + kwargs.setdefault("is_primary", True) + if not isinstance(getattr(class_, '__init__'), types.MethodType): + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + class_.__init__ = __init__ + extension = kwargs.pop('extension', None) + if extension is not None: + extension = util.to_list(extension) + extension.append(ctx.mapper_extension) + else: + extension = ctx.mapper_extension + m = mapper(class_, extension=extension, *args, **kwargs) + class_.mapper = m + for name in ['get', 'select', 'select_by', 'selectone', 'get_by', 'join_to', 'join_via']: + monkeypatch_query_method(ctx, class_, name) + for name in ['flush', 'delete', 'expire', 'refresh', 'expunge', 'merge', 'update', 'save_or_update']: + monkeypatch_objectstore_method(ctx, class_, name) </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsthreadlocalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/threadlocal.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -1,5 +1,6 @@ </span><span class="cx"> from sqlalchemy import util, engine, mapper </span><span class="cx"> from sqlalchemy.ext.sessioncontext import SessionContext </span><ins>+import sqlalchemy.ext.assignmapper as assignmapper </ins><span class="cx"> from sqlalchemy.orm.mapper import global_extensions </span><span class="cx"> from sqlalchemy.orm.session import Session </span><span class="cx"> import sqlalchemy </span><span class="lines">@@ -25,34 +26,11 @@ </span><span class="cx"> def get_session(self): </span><span class="cx"> return self.current </span><span class="cx"> </span><del>-def monkeypatch_query_method(class_, name): - def do(self, *args, **kwargs): - query = class_.mapper.query() - getattr(query, name)(*args, **kwargs) - setattr(class_, name, classmethod(do)) - -def monkeypatch_objectstore_method(class_, name): - def do(self, *args, **kwargs): - session = sqlalchemy.objectstore.current - getattr(session, name)(self, *args, **kwargs) - setattr(class_, name, do) - </del><span class="cx"> def assign_mapper(class_, *args, **kwargs): </span><del>- kwargs.setdefault("is_primary", True) - if not isinstance(getattr(class_, '__init__'), types.MethodType): - def __init__(self, **kwargs): - for key, value in kwargs.items(): - setattr(self, key, value) - class_.__init__ = __init__ - m = mapper(class_, *args, **kwargs) - class_.mapper = m - for name in ['get', 'select', 'select_by', 'selectone', 'get_by']: - monkeypatch_query_method(class_, name) - for name in ['flush', 'delete', 'expire', 'refresh', 'expunge', 'merge', 'update', 'save_or_update']: - monkeypatch_objectstore_method(class_, name) </del><ins>+ assignmapper.assign_mapper(objectstore, class_, *args, **kwargs) </ins><span class="cx"> </span><span class="cx"> def _mapper_extension(): </span><del>- return SessionContext._get_mapper_extension(sqlalchemy.objectstore) </del><ins>+ return SessionContext._get_mapper_extension(objectstore) </ins><span class="cx"> </span><span class="cx"> objectstore = Objectstore(Session) </span><span class="cx"> def install_plugin(): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -53,20 +53,31 @@ </span><span class="cx"> concrete=False, </span><span class="cx"> select_table=None): </span><span class="cx"> </span><del>- ext = MapperExtension() - </del><ins>+ # uber-pendantic style of making mapper chain, as various testbase/ + # threadlocal/assignmapper combinations keep putting dupes etc. in the list + # TODO: do something that isnt 21 lines.... + extlist = util.HashSet() </ins><span class="cx"> for ext_class in global_extensions: </span><span class="cx"> if isinstance(ext_class, MapperExtension): </span><del>- ext = ext_class.chain(ext) </del><ins>+ extlist.append(ext_class) </ins><span class="cx"> else: </span><del>- ext = ext_class().chain(ext) </del><ins>+ extlist.append(ext_class()) </ins><span class="cx"> </span><span class="cx"> if extension is not None: </span><span class="cx"> for ext_obj in util.to_list(extension): </span><del>- ext = ext_obj.chain(ext) </del><ins>+ extlist.append(ext_obj) + + self.extension = None + previous = None + for ext in extlist: + if self.extension is None: + self.extension = ext + if previous is not None: + previous.chain(ext) + previous = ext + if self.extension is None: + self.extension = MapperExtension() </ins><span class="cx"> </span><del>- self.extension = ext - </del><span class="cx"> self.class_ = class_ </span><span class="cx"> self.entity_name = entity_name </span><span class="cx"> self.class_key = ClassKey(class_, entity_name) </span><span class="lines">@@ -954,8 +965,10 @@ </span><span class="cx"> def __init__(self): </span><span class="cx"> self.next = None </span><span class="cx"> def chain(self, ext): </span><ins>+ if ext is self: + raise "nu uh " + repr(self) + " " + repr(ext) </ins><span class="cx"> self.next = ext </span><del>- return self </del><ins>+ return self </ins><span class="cx"> def get_session(self): </span><span class="cx"> """called to retrieve a contextual Session instance with which to </span><span class="cx"> register a new object. Note: this is not called if a session is </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -60,7 +60,7 @@ </span><span class="cx"> for t in self.connections.values(): </span><span class="cx"> t[0].close() </span><span class="cx"> self.session.transaction = None </span><del>- </del><ins>+ </ins><span class="cx"> class Session(object): </span><span class="cx"> """encapsulates a set of objects being operated upon within an object-relational operation.""" </span><span class="cx"> def __init__(self, bind_to=None, hash_key=None, import_session=None, echo_uow=False): </span><span class="lines">@@ -366,7 +366,7 @@ </span><span class="cx"> if getattr(obj, '_sa_session_id', None) != self.hash_key: </span><span class="cx"> old = getattr(obj, '_sa_session_id', None) </span><span class="cx"> if old is not None: </span><del>- raise exceptions.InvalidRequestError("Object '%s' is already attached to session '%s'" % (repr(obj), old)) </del><ins>+ raise exceptions.InvalidRequestError("Object '%s' is already attached to session '%s' (this is '%s')" % (repr(obj), old, id(self))) </ins><span class="cx"> </span><span class="cx"> # auto-removal from the old session is disabled. but if we decide to </span><span class="cx"> # turn it back on, do it as below: gingerly since _sessions is a WeakValueDict </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormtopologicalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/topological.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/topological.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/topological.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -102,7 +102,7 @@ </span><span class="cx"> n.cycles = Set([n]) </span><span class="cx"> continue </span><span class="cx"> else: </span><del>- raise CommitError("Self-referential dependency detected " + repr(t)) </del><ins>+ raise FlushError("Self-referential dependency detected " + repr(t)) </ins><span class="cx"> childnode = nodes[t[1]] </span><span class="cx"> parentnode = nodes[t[0]] </span><span class="cx"> self._add_edge(edges, (parentnode, childnode)) </span><span class="lines">@@ -136,7 +136,7 @@ </span><span class="cx"> continue </span><span class="cx"> else: </span><span class="cx"> # long cycles not allowed </span><del>- raise CommitError("Circular dependency detected " + repr(edges) + repr(queue)) </del><ins>+ raise FlushError("Circular dependency detected " + repr(edges) + repr(queue)) </ins><span class="cx"> node = queue.pop() </span><span class="cx"> if not hasattr(node, '_cyclical'): </span><span class="cx"> output.append(node) </span><span class="lines">@@ -328,7 +328,7 @@ </span><span class="cx"> elif parentnode.is_descendant_of(childnode): </span><span class="cx"> # check for a line thats backwards with nodes in between, this is a </span><span class="cx"> # circular dependency (although confirmation on this would be helpful) </span><del>- raise CommitError("Circular dependency detected") </del><ins>+ raise FlushError("Circular dependency detected") </ins><span class="cx"> elif not childnode.is_descendant_of(parentnode): </span><span class="cx"> # if relationship doesnt exist, connect nodes together </span><span class="cx"> root = childnode.get_sibling_ancestor(parentnode) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -695,7 +695,7 @@ </span><span class="cx"> self.__engines[engine_or_url] = engine_or_url </span><span class="cx"> self.context._engine = engine_or_url </span><span class="cx"> def is_bound(self): </span><del>- return s.context._engine is not None </del><ins>+ return self.context._engine is not None </ins><span class="cx"> def dispose(self): </span><span class="cx"> """disposes all Engines to which this DynamicMetaData has been connected.""" </span><span class="cx"> for e in self.__engines.values(): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/util.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/lib/sqlalchemy/util.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -257,44 +257,55 @@ </span><span class="cx"> # first mark everything current as "deleted" </span><span class="cx"> for i in self.data: </span><span class="cx"> self.records[i] = False </span><ins>+ self.do_value_deleted(value) </ins><span class="cx"> </span><span class="cx"> # switch array </span><span class="cx"> self.data = data </span><span class="cx"> </span><span class="cx"> # TODO: fix this up, remove items from array while iterating </span><span class="cx"> for i in range(0, len(self.data)): </span><del>- if not self._setrecord(self.data[i]): - del self.data[i] - i -= 1 </del><ins>+ if not self.__setrecord(self.data[i], False): + del self.data[i] + i -= 1 + for item in self.data: + self.do_value_appended(item) </ins><span class="cx"> def history_contains(self, obj): </span><span class="cx"> """returns true if the given object exists within the history </span><span class="cx"> for this HistoryArrayList.""" </span><span class="cx"> return self.records.has_key(obj) </span><span class="cx"> def __hash__(self): </span><span class="cx"> return id(self) </span><del>- def _setrecord(self, item): - if self.readonly: - raise InvalidRequestError("This list is read only") </del><ins>+ def do_value_appended(self, value): + pass + def do_value_deleted(self, value): + pass + def __setrecord(self, item, dochanged=True): </ins><span class="cx"> try: </span><span class="cx"> val = self.records[item] </span><span class="cx"> if val is True or val is None: </span><span class="cx"> return False </span><span class="cx"> else: </span><span class="cx"> self.records[item] = None </span><ins>+ if dochanged: + self.do_value_appended(item) </ins><span class="cx"> return True </span><span class="cx"> except KeyError: </span><span class="cx"> self.records[item] = True </span><ins>+ if dochanged: + self.do_value_appended(item) </ins><span class="cx"> return True </span><del>- def _delrecord(self, item): - if self.readonly: - raise InvalidRequestError("This list is read only") </del><ins>+ def __delrecord(self, item, dochanged=True): </ins><span class="cx"> try: </span><span class="cx"> val = self.records[item] </span><span class="cx"> if val is None: </span><span class="cx"> self.records[item] = False </span><ins>+ if dochanged: + self.do_value_deleted(item) </ins><span class="cx"> return True </span><span class="cx"> elif val is True: </span><span class="cx"> del self.records[item] </span><ins>+ if dochanged: + self.do_value_deleted(item) </ins><span class="cx"> return True </span><span class="cx"> return False </span><span class="cx"> except KeyError: </span><span class="lines">@@ -350,10 +361,10 @@ </span><span class="cx"> def has_item(self, item): </span><span class="cx"> return self.records.has_key(item) and self.records[item] is not False </span><span class="cx"> def __setitem__(self, i, item): </span><del>- if self._setrecord(item): </del><ins>+ if self.__setrecord(item): </ins><span class="cx"> self.data[i] = item </span><span class="cx"> def __delitem__(self, i): </span><del>- self._delrecord(self.data[i]) </del><ins>+ self.__delrecord(self.data[i]) </ins><span class="cx"> del self.data[i] </span><span class="cx"> def __setslice__(self, i, j, other): </span><span class="cx"> i = max(i, 0); j = max(j, 0) </span><span class="lines">@@ -363,25 +374,25 @@ </span><span class="cx"> l = other </span><span class="cx"> else: </span><span class="cx"> l = list(other) </span><del>- g = [a for a in l if self._setrecord(a)] </del><ins>+ g = [a for a in l if self.__setrecord(a)] </ins><span class="cx"> self.data[i:] = g </span><span class="cx"> def __delslice__(self, i, j): </span><span class="cx"> i = max(i, 0); j = max(j, 0) </span><span class="cx"> for a in self.data[i:j]: </span><del>- self._delrecord(a) </del><ins>+ self.__delrecord(a) </ins><span class="cx"> del self.data[i:j] </span><span class="cx"> def append(self, item): </span><del>- if self._setrecord(item): </del><ins>+ if self.__setrecord(item): </ins><span class="cx"> self.data.append(item) </span><span class="cx"> def insert(self, i, item): </span><del>- if self._setrecord(item): </del><ins>+ if self.__setrecord(item): </ins><span class="cx"> self.data.insert(i, item) </span><span class="cx"> def pop(self, i=-1): </span><span class="cx"> item = self.data[i] </span><del>- if self._delrecord(item): </del><ins>+ if self.__delrecord(item): </ins><span class="cx"> return self.data.pop(i) </span><span class="cx"> def remove(self, item): </span><del>- if self._delrecord(item): </del><ins>+ if self.__delrecord(item): </ins><span class="cx"> self.data.remove(item) </span><span class="cx"> def extend(self, item_list): </span><span class="cx"> for item in item_list: </span></span></pre></div> <a id="sqlalchemybranchesschematestactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/activemapper.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/activemapper.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/test/activemapper.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -1,80 +1,83 @@ </span><del>-from activemapper import ActiveMapper, column, one_to_many, one_to_one -from sqlalchemy import objectstore -from sqlalchemy import and_, or_ </del><ins>+from sqlalchemy.ext.activemapper import ActiveMapper, column, one_to_many, one_to_one, objectstore +from sqlalchemy import and_, or_, clear_mappers </ins><span class="cx"> from sqlalchemy import ForeignKey, String, Integer, DateTime </span><span class="cx"> from datetime import datetime </span><span class="cx"> </span><span class="cx"> import unittest </span><del>-import activemapper </del><ins>+import sqlalchemy.ext.activemapper as activemapper </ins><span class="cx"> </span><del>-# -# application-level model objects -# </del><ins>+import testbase </ins><span class="cx"> </span><del>-class Person(ActiveMapper): - class mapping: - id = column(Integer, primary_key=True) - full_name = column(String) - first_name = column(String) - middle_name = column(String) - last_name = column(String) - birth_date = column(DateTime) - ssn = column(String) - gender = column(String) - home_phone = column(String) - cell_phone = column(String) - work_phone = column(String) - prefs_id = column(Integer, foreign_key=ForeignKey('preferences.id')) - addresses = one_to_many('Address', colname='person_id', backref='person') - preferences = one_to_one('Preferences', colname='pref_id', backref='person') - - def __str__(self): - s = '%s\n' % self.full_name - s += ' * birthdate: %s\n' % (self.birth_date or 'not provided') - s += ' * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown') - s += ' * personality: %s\n' % (self.preferences.personality_type or 'Unknown') </del><ins>+class testcase(testbase.PersistTest): + def setUpAll(self): + global Person, Preferences, Address </ins><span class="cx"> </span><del>- for address in self.addresses: - s += ' * address: %s\n' % address.address_1 - s += ' %s, %s %s\n' % (address.city, address.state, address.postal_code) - - return s </del><ins>+ class Person(ActiveMapper): + class mapping: + id = column(Integer, primary_key=True) + full_name = column(String) + first_name = column(String) + middle_name = column(String) + last_name = column(String) + birth_date = column(DateTime) + ssn = column(String) + gender = column(String) + home_phone = column(String) + cell_phone = column(String) + work_phone = column(String) + prefs_id = column(Integer, foreign_key=ForeignKey('preferences.id')) + addresses = one_to_many('Address', colname='person_id', backref='person') + preferences = one_to_one('Preferences', colname='pref_id', backref='person') </ins><span class="cx"> </span><ins>+ def __str__(self): + s = '%s\n' % self.full_name + s += ' * birthdate: %s\n' % (self.birth_date or 'not provided') + s += ' * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown') + s += ' * personality: %s\n' % (self.preferences.personality_type or 'Unknown') </ins><span class="cx"> </span><del>-class Preferences(ActiveMapper): - class mapping: - __table__ = 'preferences' - id = column(Integer, primary_key=True) - favorite_color = column(String) - personality_type = column(String) </del><ins>+ for address in self.addresses: + s += ' * address: %s\n' % address.address_1 + s += ' %s, %s %s\n' % (address.city, address.state, address.postal_code) </ins><span class="cx"> </span><ins>+ return s </ins><span class="cx"> </span><del>-class Address(ActiveMapper): - class mapping: - id = column(Integer, primary_key=True) - type = column(String) - address_1 = column(String) - city = column(String) - state = column(String) - postal_code = column(String) - person_id = column(Integer, foreign_key=ForeignKey('person.id')) </del><ins>+ class Preferences(ActiveMapper): + class mapping: + __table__ = 'preferences' + id = column(Integer, primary_key=True) + favorite_color = column(String) + personality_type = column(String) </ins><span class="cx"> </span><ins>+ class Address(ActiveMapper): + class mapping: + id = column(Integer, primary_key=True) + type = column(String) + address_1 = column(String) + city = column(String) + state = column(String) + postal_code = column(String) + person_id = column(Integer, foreign_key=ForeignKey('person.id')) </ins><span class="cx"> </span><ins>+ activemapper.metadata.connect(testbase.db) + activemapper.create_tables() </ins><span class="cx"> </span><del>-class testcase(unittest.TestCase): - </del><ins>+ def tearDownAll(self): + clear_mappers() + </ins><span class="cx"> def tearDown(self): </span><del>- people = Person.select() - for person in people: person.delete() </del><ins>+ for t in activemapper.metadata.table_iterator(reverse=True): + t.delete().execute() + #people = Person.select() + #for person in people: person.delete() </ins><span class="cx"> </span><del>- addresses = Address.select() - for address in addresses: address.delete() </del><ins>+ #addresses = Address.select() + #for address in addresses: address.delete() </ins><span class="cx"> </span><del>- preferences = Preferences.select() - for preference in preferences: preference.delete() </del><ins>+ #preferences = Preferences.select() + #for preference in preferences: preference.delete() </ins><span class="cx"> </span><del>- objectstore.commit() - objectstore.clear() </del><ins>+ #objectstore.flush() + #objectstore.clear() </ins><span class="cx"> </span><span class="cx"> def create_person_one(self): </span><span class="cx"> # create a person </span><span class="lines">@@ -131,8 +134,7 @@ </span><span class="cx"> </span><span class="cx"> def test_create(self): </span><span class="cx"> p1 = self.create_person_one() </span><del>- - objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><span class="cx"> results = Person.select() </span><span class="lines">@@ -148,14 +150,14 @@ </span><span class="cx"> def test_delete(self): </span><span class="cx"> p1 = self.create_person_one() </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><span class="cx"> results = Person.select() </span><span class="cx"> self.assertEquals(len(results), 1) </span><span class="cx"> </span><span class="cx"> results[0].delete() </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><span class="cx"> results = Person.select() </span><span class="lines">@@ -166,7 +168,7 @@ </span><span class="cx"> p1 = self.create_person_one() </span><span class="cx"> p2 = self.create_person_two() </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><span class="cx"> # select and make sure we get back two results </span><span class="lines">@@ -197,25 +199,31 @@ </span><span class="cx"> # FIXME: I don't know why, but it seems that my backwards relationship </span><span class="cx"> # on preferences still ends up being a list even though I pass </span><span class="cx"> # in uselist=False... </span><ins>+ # FIXED: the backref is a new PropertyLoader which needs its own "uselist". + # uses a function which I dont think existed when you first wrote ActiveMapper. </ins><span class="cx"> p1 = self.create_person_one() </span><span class="cx"> self.assertEquals(p1.preferences.person, p1) </span><span class="cx"> p1.delete() </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> def test_select_by(self): </span><span class="cx"> # FIXME: either I don't understand select_by, or it doesn't work. </span><ins>+ # FIXED (as good as we can for now): yup....everyone thinks it works that way....it only + # generates joins for keyword arguments, not ColumnClause args. would need a new layer of + # "MapperClause" objects to use properties in expressions. (MB) </ins><span class="cx"> </span><span class="cx"> p1 = self.create_person_one() </span><span class="cx"> p2 = self.create_person_two() </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ objectstore.flush() </ins><span class="cx"> objectstore.clear() </span><span class="cx"> </span><del>- results = Person.select_by( - Address.c.postal_code.like('30075') </del><ins>+ results = Person.select( + Address.c.postal_code.like('30075') & + Person.join_to('addresses') </ins><span class="cx"> ) </span><span class="cx"> self.assertEquals(len(results), 1) </span><span class="cx"> </span><span class="lines">@@ -223,8 +231,6 @@ </span><span class="cx"> </span><span class="cx"> if __name__ == '__main__': </span><span class="cx"> # go ahead and setup the database connection, and create the tables </span><del>- activemapper.engine.connect('sqlite:///', echo=False) - activemapper.create_tables() </del><span class="cx"> </span><span class="cx"> # launch the unit tests </span><span class="cx"> unittest.main() </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/test/alltests.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -56,6 +56,7 @@ </span><span class="cx"> </span><span class="cx"> # extensions </span><span class="cx"> 'proxy_engine', </span><ins>+ 'activemapper' </ins><span class="cx"> #'wsgi_test', </span><span class="cx"> </span><span class="cx"> ) </span></span></pre></div> <a id="sqlalchemybranchesschematestcyclespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/cycles.py (1481 => 1482)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/cycles.py 2006-05-21 18:34:12 UTC (rev 1481) +++ sqlalchemy/branches/schema/test/cycles.py 2006-05-21 21:17:58 UTC (rev 1482) </span><span class="lines">@@ -22,26 +22,22 @@ </span><span class="cx"> class SelfReferentialTest(AssertMixin): </span><span class="cx"> """tests a self-referential mapper, with an additional list of child objects.""" </span><span class="cx"> def setUpAll(self): </span><del>- testbase.metadata.tables.clear() - global t1 - global t2 - t1 = Table('t1', testbase.metadata, </del><ins>+ global t1, t2, metadata + metadata = BoundMetaData(testbase.db) + t1 = Table('t1', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('parent_c1', Integer, ForeignKey('t1.c1')), </span><span class="cx"> Column('data', String(20)) </span><span class="cx"> ) </span><del>- t2 = Table('t2', testbase.metadata, </del><ins>+ t2 = Table('t2', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('c1id', Integer, ForeignKey('t1.c1')), </span><span class="cx"> Column('data', String(20)) </span><span class="cx"> ) </span><del>- t1.create() - t2.create() </del><ins>+ metadata.create_all() </ins><span class="cx"> def tearDownAll(self): </span><del>- t2.drop() - t1.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> def setUp(self): </span><del>- objectstore.clear() </del><span class="cx"> clear_mappers() </span><span class="cx"> </span><span class="cx"> def testsingle(self): </span><span class="lines">@@ -53,9 +49,11 @@ </span><span class="cx"> }) </span><span class="cx"> a = C1('head c1') </span><span class="cx"> a.c1s.append(C1('another c1')) </span><del>- objectstore.flush() - objectstore.delete(a) - objectstore.flush() </del><ins>+ sess = create_session() + sess.save(a) + sess.flush() + sess.delete(a) + sess.flush() </ins><span class="cx"> </span><span class="cx"> def testcycle(self): </span><span class="cx"> class C1(Tester): </span><span class="lines">@@ -75,36 +73,34 @@ </span><span class="cx"> a.c1s[0].c1s.append(C1('subchild2')) </span><span class="cx"> a.c1s[1].c2s.append(C2('child2 data1')) </span><span class="cx"> a.c1s[1].c2s.append(C2('child2 data2')) </span><del>- objectstore.flush() </del><ins>+ sess = create_session() + sess.save(a) + sess.flush() </ins><span class="cx"> </span><del>- objectstore.delete(a) - objectstore.flush() </del><ins>+ sess.delete(a) + sess.flush() </ins><span class="cx"> </span><span class="cx"> class BiDirectionalOneToManyTest(AssertMixin): </span><span class="cx"> """tests two mappers with a one-to-many relation to each other.""" </span><span class="cx"> def setUpAll(self): </span><del>- testbase.metadata.tables.clear() - global t1 - global t2 - t1 = Table('t1', testbase.metadata, </del><ins>+ global t1, t2, metadata + metadata = BoundMetaData(testbase.db) + t1 = Table('t1', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('c2', Integer, ForeignKey('t2.c1')) </span><span class="cx"> ) </span><del>- t2 = Table('t2', testbase.metadata, </del><ins>+ t2 = Table('t2', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('c2', Integer) </span><span class="cx"> ) </span><del>- t2.create() - t1.create() </del><ins>+ metadata.create_all() </ins><span class="cx"> t2.c.c2.append_item(ForeignKey('t1.c1')) </span><span class="cx"> def tearDownAll(self): </span><del>- t1.drop() </del><ins>+ t1.drop() </ins><span class="cx"> t2.drop() </span><del>- def setUp(self): - objectstore.clear() - #objectstore.LOG = True </del><ins>+ #metadata.drop_all() + def tearDown(self): </ins><span class="cx"> clear_mappers() </span><del>- </del><span class="cx"> def testcycle(self): </span><span class="cx"> class C1(object):pass </span><span class="cx"> class C2(object):pass </span><span class="lines">@@ -123,35 +119,33 @@ </span><span class="cx"> a.c2s.append(b) </span><span class="cx"> d.c1s.append(c) </span><span class="cx"> b.c1s.append(c) </span><del>- objectstore.flush() </del><ins>+ sess = create_session() + [sess.save(x) for x in [a,b,c,d,e,f]] + sess.flush() </ins><span class="cx"> </span><span class="cx"> class BiDirectionalOneToManyTest2(AssertMixin): </span><span class="cx"> """tests two mappers with a one-to-many relation to each other, with a second one-to-many on one of the mappers""" </span><span class="cx"> def setUpAll(self): </span><del>- testbase.metadata.tables.clear() - global t1 - global t2 - global t3 - t1 = Table('t1', testbase.metadata, </del><ins>+ global t1, t2, t3, metadata + metadata = BoundMetaData(testbase.db) + t1 = Table('t1', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('c2', Integer, ForeignKey('t2.c1')), </span><span class="cx"> ) </span><del>- t2 = Table('t2', testbase.metadata, </del><ins>+ t2 = Table('t2', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('c2', Integer), </span><span class="cx"> ) </span><span class="cx"> t2.create() </span><span class="cx"> t1.create() </span><span class="cx"> t2.c.c2.append_item(ForeignKey('t1.c1')) </span><del>- t3 = Table('t1_data', testbase.metadata, </del><ins>+ t3 = Table('t1_data', metadata, </ins><span class="cx"> Column('c1', Integer, primary_key=True), </span><span class="cx"> Column('t1id', Integer, ForeignKey('t1.c1')), </span><span class="cx"> Column('data', String(20))) </span><span class="cx"> t3.create() </span><span class="cx"> </span><del>- def setUp(self): - objectstore.clear() - #objectstore.LOG = True </del><ins>+ def tearDown(self): </ins><span class="cx"> clear_mappers() </span><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><span class="lines">@@ -185,25 +179,28 @@ </span><span class="cx"> a.data.append(C1Data('c1data1')) </span><span class="cx"> a.data.append(C1Data('c1data2')) </span><span class="cx"> c.data.append(C1Data('c1data3')) </span><del>- objectstore.flush() </del><ins>+ sess = create_session() + [sess.save(x) for x in [a,b,c,d,e,f]] + sess.flush() </ins><span class="cx"> </span><del>- objectstore.delete(d) - objectstore.delete(c) - objectstore.flush() </del><ins>+ sess.delete(d) + sess.delete(c) + sess.flush() </ins><span class="cx"> </span><span class="cx"> class OneToManyManyToOneTest(AssertMixin): </span><span class="cx"> """tests two mappers, one has a one-to-many on the other mapper, the other has a separate many-to-one relationship to the first. </span><span class="cx"> two tests will have a row for each item that is dependent on the other. without the "post_update" flag, such relationships </span><span class="cx"> raise an exception when dependencies are sorted.""" </span><span class="cx"> def setUpAll(self): </span><del>- testbase.metadata.tables.clear() </del><ins>+ global metadata + metadata = BoundMetaData(testbase.db) </ins><span class="cx"> global person </span><span class="cx"> global ball </span><del>- ball = Table('ball', db, </del><ins>+ ball = Table('ball', metadata, </ins><span class="cx"> Column('id', Integer, Sequence('ball_id_seq', optional=True), primary_key=True), </span><span class="cx"> Column('person_id', Integer), </span><span class="cx"> ) </span><del>- person = Table('person', db, </del><ins>+ person = Table('person', metadata, </ins><span class="cx"> Column('id', Integer, Sequence('person_id_seq', optional=True), primary_key=True), </span><span class="cx"> Column('favoriteBall_id', Integer, ForeignKey('ball.id')), </span><span class="cx"> # Column('favoriteBall_id', Integer), </span><span class="lines">@@ -223,9 +220,7 @@ </span><span class="cx"> person.drop() </span><span class="cx"> ball.drop() </span><span class="cx"> </span><del>- def setUp(self): - objectstore.clear() - #objectstore.LOG = True </del><ins>+ def tearDown(self): </ins><span class="cx"> clear_mappers() </span><span class="cx"> </span><span class="cx"> def testcycle(self): </span><span class="lines">@@ -249,7 +244,10 @@ </span><span class="cx"> b = Ball() </span><span class="cx"> p = Person() </span><span class="cx"> p.balls.append(b) </span><del>- objectstore.flush() </del><ins>+ sess = create_session() + sess.save(b) + sess.save(b) + sess.flush() </ins><span class="cx"> </span><span class="cx"> def testpostupdate_m2o(self): </span><span class="cx"> """tests a cycle between two rows, with a post_update on the many-to-one""" </span><span class="lines">@@ -275,8 +273,11 @@ </span><span class="cx"> p.balls.append(Ball()) </span><span class="cx"> p.balls.append(Ball()) </span><span class="cx"> p.favorateBall = b </span><del>- - self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </del><ins>+ sess = create_session() + sess.save(b) + sess.save(p) + + self.assert_sql(db, lambda: sess.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)", </span><span class="cx"> {'favoriteBall_id': None} </span><span class="lines">@@ -329,8 +330,8 @@ </span><span class="cx"> lambda ctx:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id} </span><span class="cx"> ) </span><span class="cx"> ]) </span><del>- objectstore.delete(p) - self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </del><ins>+ sess.delete(p) + self.assert_sql(db, lambda: sess.flush(), [ </ins><span class="cx"> # heres the post update (which is a pre-update with deletes) </span><span class="cx"> ( </span><span class="cx"> "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", </span><span class="lines">@@ -377,7 +378,10 @@ </span><span class="cx"> b4 = Ball() </span><span class="cx"> p.balls.append(b4) </span><span class="cx"> p.favorateBall = b </span><del>- self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </del><ins>+ sess = create_session() + [sess.save(x) for x in [b,p,b2,b3,b4]] + + self.assert_sql(db, lambda: sess.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "INSERT INTO ball (person_id) VALUES (:person_id)", </span><span class="cx"> {'person_id':None} </span><span class="lines">@@ -455,8 +459,8 @@ </span><span class="cx"> ), </span><span class="cx"> ]) </span><span class="cx"> </span><del>- objectstore.delete(p) - self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </del><ins>+ sess.delete(p) + self.assert_sql(db, lambda: sess.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", </span><span class="cx"> lambda ctx:{'person_id': None, 'ball_id': b.id} </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1481 => 1482)</h4> <pre class="diff"><... [truncated message content] |
From: <co...@sq...> - 2006-05-21 18:34:22
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1481] sqlalchemy/branches/schema/test/objectstore.py: more oracle tweaks</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1481</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 13:34:12 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>more oracle tweaks</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1480 => 1481)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-05-21 18:17:55 UTC (rev 1480) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-05-21 18:34:12 UTC (rev 1481) </span><span class="lines">@@ -91,7 +91,7 @@ </span><span class="cx"> ctx.current.clear() </span><span class="cx"> global version_table </span><span class="cx"> version_table = Table('version_test', db, </span><del>- Column('id', Integer, primary_key=True), </del><ins>+ Column('id', Integer, Sequence('version_test_seq'), primary_key=True ), </ins><span class="cx"> Column('version_id', Integer, nullable=False), </span><span class="cx"> Column('value', String(40), nullable=False) </span><span class="cx"> ).create() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-21 18:18:09
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1480] sqlalchemy/branches/schema/test: oracle working about as well as it did with 0.1.....should get more unittests up with it</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1480</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 13:17:55 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>oracle working about as well as it did with 0.1.....should get more unittests up with it</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesoraclepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestdefaultspy">sqlalchemy/branches/schema/test/defaults.py</a></li> <li><a href="#sqlalchemybranchesschematestquerypy">sqlalchemy/branches/schema/test/query.py</a></li> <li><a href="#sqlalchemybranchesschematestselectpy">sqlalchemy/branches/schema/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -110,14 +110,14 @@ </span><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> class OracleDialect(ansisql.ANSIDialect): </span><del>- def __init__(self, use_ansi=True, module=None, threaded=True, **params): </del><ins>+ def __init__(self, use_ansi=True, module=None, threaded=True, **kwargs): </ins><span class="cx"> self.use_ansi = use_ansi </span><span class="cx"> self.threaded = threaded </span><span class="cx"> if module is None: </span><span class="cx"> self.module = cx_Oracle </span><span class="cx"> else: </span><span class="cx"> self.module = module </span><del>- ansisql.ANSISQLEngine.__init__(self, **params) </del><ins>+ ansisql.ANSIDialect.__init__(self, **kwargs) </ins><span class="cx"> </span><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="lines">@@ -147,11 +147,11 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> def has_table(self, connection, table_name): </span><del>- cursor = connection.execute("""select table_name from all_tables where table_name=:table_name""", {'name':table_name}) - return bool( not not cursor.rowcount ) </del><ins>+ cursor = connection.execute("""select table_name from all_tables where table_name=:name""", {'name':table_name.upper()}) + return bool( cursor.fetchone() is not None ) </ins><span class="cx"> </span><del>- def reflecttable(self, table): - c = self.execute ("select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT from ALL_TAB_COLUMNS where TABLE_NAME = :table_name", {'table_name':table.name.upper()}) </del><ins>+ def reflecttable(self, connection, table): + c = connection.execute ("select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT from ALL_TAB_COLUMNS where TABLE_NAME = :table_name", {'table_name':table.name.upper()}) </ins><span class="cx"> </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="lines">@@ -180,14 +180,14 @@ </span><span class="cx"> </span><span class="cx"> colargs = [] </span><span class="cx"> if default is not None: </span><del>- colargs.append(PassiveDefault(sql.text(default))) </del><ins>+ colargs.append(schema.PassiveDefault(sql.text(default))) </ins><span class="cx"> </span><span class="cx"> name = name.lower() </span><span class="cx"> </span><span class="cx"> table.append_item (schema.Column(name, coltype, nullable=nullable, *colargs)) </span><span class="cx"> </span><span class="cx"> </span><del>- c = self.execute(constraintSQL, {'table_name' : table.name.upper()}) </del><ins>+ c = connection.execute(constraintSQL, {'table_name' : table.name.upper()}) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -198,8 +198,8 @@ </span><span class="cx"> table.c[local_column]._set_primary_key() </span><span class="cx"> elif cons_type == 'R': </span><span class="cx"> table.c[local_column].append_item( </span><del>- schema.ForeignKey(Table(remote_table, - self, </del><ins>+ schema.ForeignKey(schema.Table(remote_table, + table.metadata, </ins><span class="cx"> autoload=True).c[remote_column] </span><span class="cx"> ) </span><span class="cx"> ) </span><span class="lines">@@ -281,6 +281,8 @@ </span><span class="cx"> orderby = self.strings[select.order_by_clause] </span><span class="cx"> if not orderby: </span><span class="cx"> orderby = select.oid_column </span><ins>+ orderby.accept_visitor(self) + orderby = self.strings[orderby] </ins><span class="cx"> select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) </span><span class="cx"> limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) </span><span class="cx"> if select.offset is not None: </span><span class="lines">@@ -330,3 +332,5 @@ </span><span class="cx"> </span><span class="cx"> def visit_sequence(self, seq): </span><span class="cx"> return self.proxy("SELECT " + seq.name + ".nextval FROM DUAL").fetchone()[0] </span><ins>+ +dialect = OracleDialect </ins></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -201,8 +201,8 @@ </span><span class="cx"> def __init__(self, module=None, use_oids=False, **params): </span><span class="cx"> self.use_oids = use_oids </span><span class="cx"> if module is None: </span><del>- if psycopg is None: - raise exceptions.ArgumentError("Couldnt locate psycopg1 or psycopg2: specify postgres module argument") </del><ins>+ #if psycopg is None: + # raise exceptions.ArgumentError("Couldnt locate psycopg1 or psycopg2: specify postgres module argument") </ins><span class="cx"> self.module = psycopg </span><span class="cx"> else: </span><span class="cx"> self.module = module </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -965,7 +965,8 @@ </span><span class="cx"> self.__names.append(name) </span><span class="cx"> return self </span><span class="cx"> def __call__(self, *c, **kwargs): </span><del>- return Function(self.__names[-1], packagenames=self.__names[0:-1], engine=self.__engine, *c, **kwargs) </del><ins>+ kwargs.setdefault('engine', self.__engine) + return Function(self.__names[-1], packagenames=self.__names[0:-1], *c, **kwargs) </ins><span class="cx"> </span><span class="cx"> class BinaryClause(ClauseElement): </span><span class="cx"> """represents two clauses with an operator in between""" </span></span></pre></div> <a id="sqlalchemybranchesschematestdefaultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/defaults.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/defaults.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/test/defaults.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -20,15 +20,17 @@ </span><span class="cx"> use_function_defaults = db.engine.name == 'postgres' or db.engine.name == 'oracle' </span><span class="cx"> is_oracle = db.engine.name == 'oracle' </span><span class="cx"> </span><del>- # select "count(1)" from the DB which returns different results - # on different DBs - currenttime = db.func.current_date(type=Date); </del><ins>+ # select "count(1)" returns different results on different DBs + # also correct for "current_date" compatible as column default, value differences + currenttime = func.current_date(type=Date, engine=db); </ins><span class="cx"> if is_oracle: </span><del>- ts = db.func.sysdate().scalar() </del><ins>+ ts = db.func.trunc(func.sysdate(), column("'DAY'")).scalar() </ins><span class="cx"> f = select([func.count(1) + 5], engine=db).scalar() </span><span class="cx"> f2 = select([func.count(1) + 14], engine=db).scalar() </span><ins>+ # TODO: engine propigation across nested functions not working + currenttime = func.trunc(currenttime, column("'DAY'"), engine=db) </ins><span class="cx"> def1 = currenttime </span><del>- def2 = text("sysdate") </del><ins>+ def2 = func.trunc(text("sysdate"), column("'DAY'")) </ins><span class="cx"> deftype = Date </span><span class="cx"> elif use_function_defaults: </span><span class="cx"> f = select([func.count(1) + 5], engine=db).scalar() </span></span></pre></div> <a id="sqlalchemybranchesschematestquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/query.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/query.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/test/query.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -6,7 +6,6 @@ </span><span class="cx"> </span><span class="cx"> import tables </span><span class="cx"> db = testbase.db </span><del>-#db.echo='debug' </del><span class="cx"> from sqlalchemy import * </span><span class="cx"> from sqlalchemy.engine import ResultProxy, RowProxy </span><span class="cx"> </span><span class="lines">@@ -173,11 +172,9 @@ </span><span class="cx"> self.assertEqual(r[1], 1) </span><span class="cx"> self.assertEqual(r.keys(), ['user_name', 'user_id']) </span><span class="cx"> self.assertEqual(r.values(), ['foo', 1]) </span><del>- </del><ins>+ + @testbase.unsupported('oracle') </ins><span class="cx"> def test_column_accessor_shadow(self): </span><del>- if db.engine.__module__.endswith('oracle'): - return - </del><span class="cx"> shadowed = Table('test_shadowed', db, </span><span class="cx"> Column('shadow_id', INT, primary_key = True), </span><span class="cx"> Column('shadow_name', VARCHAR(20)), </span></span></pre></div> <a id="sqlalchemybranchesschematestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/select.py (1479 => 1480)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/select.py 2006-05-21 17:00:17 UTC (rev 1479) +++ sqlalchemy/branches/schema/test/select.py 2006-05-21 18:17:55 UTC (rev 1480) </span><span class="lines">@@ -1,6 +1,6 @@ </span><span class="cx"> </span><span class="cx"> from sqlalchemy import * </span><del>-from sqlalchemy.databases import sqlite, postgres, mysql </del><ins>+from sqlalchemy.databases import sqlite, postgres, mysql, oracle </ins><span class="cx"> from testbase import PersistTest </span><span class="cx"> import unittest, re </span><span class="cx"> </span><span class="lines">@@ -225,10 +225,9 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> def testoraclelimit(self): </span><del>- raise "notyet" - e = create_engine('oracle') - users = Table('users', e, Column('name', String(10), key='username')) - self.runtest(select([users.c.username], limit=5), "SELECT name FROM (SELECT users.name AS name, ROW_NUMBER() OVER (ORDER BY users.rowid ASC) AS ora_rn FROM users) WHERE ora_rn<=5", engine=e) </del><ins>+ metadata = MetaData() + users = Table('users', metadata, Column('name', String(10), key='username')) + self.runtest(select([users.c.username], limit=5), "SELECT name FROM (SELECT users.name AS name, ROW_NUMBER() OVER (ORDER BY users.rowid) AS ora_rn FROM users) WHERE ora_rn<=5", dialect=oracle.dialect()) </ins><span class="cx"> </span><span class="cx"> def testgroupby_and_orderby(self): </span><span class="cx"> self.runtest( </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-21 17:00:28
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1479] sqlalchemy/branches/schema/lib/sqlalchemy/databases: initial oracle conversion, not tested yet</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1479</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 12:00:17 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>initial oracle conversion, not tested yet</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesoraclepy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py (1478 => 1479)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py 2006-05-21 16:48:14 UTC (rev 1478) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/oracle.py 2006-05-21 17:00:17 UTC (rev 1479) </span><span class="lines">@@ -7,11 +7,14 @@ </span><span class="cx"> </span><span class="cx"> import sys, StringIO, string </span><span class="cx"> </span><ins>+import sqlalchemy.util as util </ins><span class="cx"> import sqlalchemy.sql as sql </span><ins>+import sqlalchemy.engine as engine +import sqlalchemy.engine.default as default </ins><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.ansisql as ansisql </span><del>-from sqlalchemy import * </del><span class="cx"> import sqlalchemy.types as sqltypes </span><ins>+import sqlalchemy.exceptions as exceptions </ins><span class="cx"> </span><span class="cx"> try: </span><span class="cx"> import cx_Oracle </span><span class="lines">@@ -93,8 +96,6 @@ </span><span class="cx"> -- order multiple primary keys correctly </span><span class="cx"> ORDER BY ac.constraint_name, loc.position""" </span><span class="cx"> </span><del>-def engine(*args, **params): - return OracleSQLEngine(*args, **params) </del><span class="cx"> </span><span class="cx"> def descriptor(): </span><span class="cx"> return {'name':'oracle', </span><span class="lines">@@ -104,12 +105,14 @@ </span><span class="cx"> ('user', 'Username', None), </span><span class="cx"> ('password', 'Password', None) </span><span class="cx"> ]} </span><ins>+ +class OracleExecutionContext(default.DefaultExecutionContext): + pass </ins><span class="cx"> </span><del>-class OracleSQLEngine(ansisql.ANSISQLEngine): - def __init__(self, opts, use_ansi = True, module = None, threaded=True, **params): - self._use_ansi = use_ansi - self.opts = self._translate_connect_args((None, 'dsn', 'user', 'password'), opts) - opts['threaded'] = threaded </del><ins>+class OracleDialect(ansisql.ANSIDialect): + def __init__(self, use_ansi=True, module=None, threaded=True, **params): + self.use_ansi = use_ansi + self.threaded = threaded </ins><span class="cx"> if module is None: </span><span class="cx"> self.module = cx_Oracle </span><span class="cx"> else: </span><span class="lines">@@ -119,27 +122,33 @@ </span><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="cx"> </span><del>- def connect_args(self): - return [[], self.opts] </del><ins>+ def create_connect_args(self, url): + opts = url.translate_connect_args([None, 'dsn', 'user', 'password']) + opts['threaded'] = self.threaded + return ([], opts) </ins><span class="cx"> </span><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> return sqltypes.adapt_type(typeobj, colspecs) </span><span class="cx"> </span><del>- def last_inserted_ids(self): - return self.context.last_inserted_ids - </del><span class="cx"> def oid_column_name(self): </span><span class="cx"> return "rowid" </span><span class="cx"> </span><ins>+ def create_execution_context(self): + return OracleExecutionContext(self) + </ins><span class="cx"> def compiler(self, statement, bindparams, **kwargs): </span><del>- return OracleCompiler(self, statement, bindparams, use_ansi=self._use_ansi, **kwargs) </del><ins>+ return OracleCompiler(self, statement, bindparams, **kwargs) + def schemagenerator(self, *args, **kwargs): + return OracleSchemaGenerator(*args, **kwargs) + def schemadropper(self, *args, **kwargs): + return OracleSchemaDropper(*args, **kwargs) + def defaultrunner(self, engine, proxy): + return OracleDefaultRunner(engine, proxy) </ins><span class="cx"> </span><del>- def schemagenerator(self, **params): - return OracleSchemaGenerator(self, **params) - def schemadropper(self, **params): - return OracleSchemaDropper(self, **params) - def defaultrunner(self, proxy): - return OracleDefaultRunner(self, proxy) </del><ins>+ + def has_table(self, connection, table_name): + cursor = connection.execute("""select table_name from all_tables where table_name=:table_name""", {'name':table_name}) + return bool( not not cursor.rowcount ) </ins><span class="cx"> </span><span class="cx"> def reflecttable(self, table): </span><span class="cx"> c = self.execute ("select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT from ALL_TAB_COLUMNS where TABLE_NAME = :table_name", {'table_name':table.name.upper()}) </span><span class="lines">@@ -195,28 +204,18 @@ </span><span class="cx"> ) </span><span class="cx"> ) </span><span class="cx"> </span><del>- def last_inserted_ids(self): - return self.context.last_inserted_ids - - def pre_exec(self, proxy, compiled, parameters, **kwargs): - pass - - def _executemany(self, c, statement, parameters): </del><ins>+ def do_executemany(self, c, statement, parameters, context=None): </ins><span class="cx"> rowcount = 0 </span><span class="cx"> for param in parameters: </span><span class="cx"> c.execute(statement, param) </span><span class="cx"> rowcount += c.rowcount </span><del>- self.context.rowcount = rowcount </del><ins>+ if context is not None: + context._rowcount = rowcount </ins><span class="cx"> </span><span class="cx"> class OracleCompiler(ansisql.ANSICompiler): </span><span class="cx"> """oracle compiler modifies the lexical structure of Select statements to work under </span><span class="cx"> non-ANSI configured Oracle databases, if the use_ansi flag is False.""" </span><span class="cx"> </span><del>- def __init__(self, engine, statement, parameters, use_ansi = True, **kwargs): - self._outertable = None - self._use_ansi = use_ansi - ansisql.ANSICompiler.__init__(self, statement, parameters, engine=engine, **kwargs) - </del><span class="cx"> def default_from(self): </span><span class="cx"> """called when a SELECT statement has no froms, and no FROM clause is to be appended. </span><span class="cx"> gives Oracle a chance to tack on a "FROM DUAL" to the string output. """ </span><span class="lines">@@ -226,7 +225,7 @@ </span><span class="cx"> return len(func.clauses) > 0 </span><span class="cx"> </span><span class="cx"> def visit_join(self, join): </span><del>- if self._use_ansi: </del><ins>+ if self.dialect.use_ansi: </ins><span class="cx"> return ansisql.ANSICompiler.visit_join(self, join) </span><span class="cx"> </span><span class="cx"> self.froms[join] = self.get_from_text(join.left) + ", " + self.get_from_text(join.right) </span><span class="lines">@@ -251,7 +250,7 @@ </span><span class="cx"> </span><span class="cx"> def visit_column(self, column): </span><span class="cx"> ansisql.ANSICompiler.visit_column(self, column) </span><del>- if not self._use_ansi and self._outertable is not None and column.table is self._outertable: </del><ins>+ if not self.dialect.use_ansi and getattr(self, '_outertable', None) is not None and column.table is self._outertable: </ins><span class="cx"> self.strings[column] = self.strings[column] + "(+)" </span><span class="cx"> </span><span class="cx"> def visit_insert(self, insert): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py (1478 => 1479)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-21 16:48:14 UTC (rev 1478) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/postgres.py 2006-05-21 17:00:17 UTC (rev 1479) </span><span class="lines">@@ -260,7 +260,6 @@ </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> </span><del>- </del><span class="cx"> def do_executemany(self, c, statement, parameters, context=None): </span><span class="cx"> """we need accurate rowcounts for updates, inserts and deletes. psycopg2 is not nice enough </span><span class="cx"> to produce this correctly for an executemany, so we do our own executemany here.""" </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-21 16:48:32
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1478] sqlalchemy/branches/schema/test: got 'alltests' to run minus oracle</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1478</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-21 11:48:14 -0500 (Sun, 21 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>got 'alltests' to run minus oracle</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt">sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestentitypy">sqlalchemy/branches/schema/test/entity.py</a></li> <li><a href="#sqlalchemybranchesschematestindexespy">sqlalchemy/branches/schema/test/indexes.py</a></li> <li><a href="#sqlalchemybranchesschematestlazytest1py">sqlalchemy/branches/schema/test/lazytest1.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> <li><a href="#sqlalchemybranchesschematestproxy_enginepy">sqlalchemy/branches/schema/test/proxy_engine.py</a></li> <li><a href="#sqlalchemybranchesschematestselectpy">sqlalchemy/branches/schema/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentsqlconstructiontxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/doc/build/content/sqlconstruction.txt 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -695,7 +695,8 @@ </span><span class="cx"> </span><span class="cx"> The sql package tries to allow free textual placement in as many ways as possible. In the examples below, note that the from_obj parameter is used only when no other information exists within the select object with which to determine table metadata. Also note that in a query where there isnt even table metadata used, the SQLEngine to be used for the query has to be explicitly specified: </span><span class="cx"> </span><del>- {python}# strings as column clauses </del><ins>+ {python} + # strings as column clauses </ins><span class="cx"> {sql}select(["user_id", "user_name"], from_obj=[users]).execute() </span><span class="cx"> SELECT user_id, user_name FROM users </span><span class="cx"> {} </span><span class="lines">@@ -736,19 +737,9 @@ </span><span class="cx"> </span><span class="cx"> # a full query </span><span class="cx"> {sql}text("select user_name from users", engine=db).execute() </span><del>- select user_name from users </del><ins>+ SELECT user_name FROM users </ins><span class="cx"> {} </span><span class="cx"> </span><del>- # or call text() off of the engine - engine.text("select user_name from users").execute() - - # execute off the engine directly - you must use the engine's native bind parameter - # style (i.e. named, pyformat, positional, etc.) - {sql}db.execute( - "select user_name from users where user_id=:user_id", - {'user_id':7}).execute() - select user_name from users where user_id=:user_id - {'user_id':7} </del><span class="cx"> </span><span class="cx"> #### Using Bind Parameters in Text Blocks {@name=textual_binds} </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -539,7 +539,6 @@ </span><span class="cx"> try: </span><span class="cx"> rec = self.props[key._label.lower()] </span><span class="cx"> except KeyError: </span><del>- print "DIDNT HAVE", key._label.lower() </del><span class="cx"> try: </span><span class="cx"> rec = self.props[key.key.lower()] </span><span class="cx"> except KeyError: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -56,7 +56,10 @@ </span><span class="cx"> ext = MapperExtension() </span><span class="cx"> </span><span class="cx"> for ext_class in global_extensions: </span><del>- ext = ext_class().chain(ext) </del><ins>+ if isinstance(ext_class, MapperExtension): + ext = ext_class.chain(ext) + else: + ext = ext_class().chain(ext) </ins><span class="cx"> </span><span class="cx"> if extension is not None: </span><span class="cx"> for ext_obj in util.to_list(extension): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -1157,10 +1157,10 @@ </span><span class="cx"> if self.__label is None: </span><span class="cx"> if self.table is not None and self.table.named_with_column(): </span><span class="cx"> self.__label = self.table.name + "_" + self.name </span><ins>+ if self.table.c.has_key(self.__label) or len(self.__label) >= 30: + self.__label = self.__label[0:24] + "_" + hex(random.randint(0, 65535))[2:] </ins><span class="cx"> else: </span><span class="cx"> self.__label = self.name </span><del>- if (self.table is not None and self.table.c.has_key(self.__label)) or len(self.__label) >= 30: - self.__label = self.__label[0:24] + "_" + hex(random.randint(0, 65535))[2:] </del><span class="cx"> return self.__label </span><span class="cx"> _label = property(_get_label) </span><span class="cx"> def accept_visitor(self, visitor): </span></span></pre></div> <a id="sqlalchemybranchesschematestentitypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/entity.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/entity.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/test/entity.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -2,6 +2,7 @@ </span><span class="cx"> import unittest </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> import testbase </span><ins>+from sqlalchemy.ext.sessioncontext import SessionContext </ins><span class="cx"> </span><span class="cx"> from tables import * </span><span class="cx"> import tables </span><span class="lines">@@ -10,38 +11,35 @@ </span><span class="cx"> """tests mappers that are constructed based on "entity names", which allows the same class </span><span class="cx"> to have multiple primary mappers """ </span><span class="cx"> def setUpAll(self): </span><del>- global user1, user2, address1, address2 - db = testbase.db - user1 = Table('user1', db, </del><ins>+ global user1, user2, address1, address2, metadata, ctx + metadata = BoundMetaData(testbase.db) + ctx = SessionContext(create_session) + + user1 = Table('user1', metadata, </ins><span class="cx"> Column('user_id', Integer, Sequence('user1_id_seq'), primary_key=True), </span><span class="cx"> Column('name', String(60), nullable=False) </span><del>- ).create() - user2 = Table('user2', db, </del><ins>+ ) + user2 = Table('user2', metadata, </ins><span class="cx"> Column('user_id', Integer, Sequence('user2_id_seq'), primary_key=True), </span><span class="cx"> Column('name', String(60), nullable=False) </span><del>- ).create() - address1 = Table('address1', db, </del><ins>+ ) + address1 = Table('address1', metadata, </ins><span class="cx"> Column('address_id', Integer, Sequence('address1_id_seq'), primary_key=True), </span><span class="cx"> Column('user_id', Integer, ForeignKey(user1.c.user_id), nullable=False), </span><span class="cx"> Column('email', String(100), nullable=False) </span><del>- ).create() - address2 = Table('address2', db, </del><ins>+ ) + address2 = Table('address2', metadata, </ins><span class="cx"> Column('address_id', Integer, Sequence('address2_id_seq'), primary_key=True), </span><span class="cx"> Column('user_id', Integer, ForeignKey(user2.c.user_id), nullable=False), </span><span class="cx"> Column('email', String(100), nullable=False) </span><del>- ).create() </del><ins>+ ) + metadata.create_all() </ins><span class="cx"> def tearDownAll(self): </span><del>- address1.drop() - address2.drop() - user1.drop() - user2.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> def tearDown(self): </span><del>- address1.delete().execute() - address2.delete().execute() - user1.delete().execute() - user2.delete().execute() - objectstore.clear() </del><span class="cx"> clear_mappers() </span><ins>+ for t in metadata.table_iterator(reverse=True): + t.delete().execute() </ins><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> """tests a pair of one-to-many mapper structures, establishing that both </span><span class="lines">@@ -50,14 +48,14 @@ </span><span class="cx"> class User(object):pass </span><span class="cx"> class Address(object):pass </span><span class="cx"> </span><del>- a1mapper = mapper(Address, address1, entity_name='address1') - a2mapper = mapper(Address, address2, entity_name='address2') </del><ins>+ a1mapper = mapper(Address, address1, entity_name='address1', extension=ctx.mapper_extension) + a2mapper = mapper(Address, address2, entity_name='address2', extension=ctx.mapper_extension) </ins><span class="cx"> u1mapper = mapper(User, user1, entity_name='user1', properties ={ </span><span class="cx"> 'addresses':relation(a1mapper) </span><del>- }) </del><ins>+ }, extension=ctx.mapper_extension) </ins><span class="cx"> u2mapper =mapper(User, user2, entity_name='user2', properties={ </span><span class="cx"> 'addresses':relation(a2mapper) </span><del>- }) </del><ins>+ }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><span class="cx"> u1 = User(_sa_entity_name='user1') </span><span class="cx"> u1.name = 'this is user 1' </span><span class="lines">@@ -71,15 +69,15 @@ </span><span class="cx"> a2.email='a2...@fo...' </span><span class="cx"> u2.addresses.append(a2) </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ ctx.current.flush() </ins><span class="cx"> assert user1.select().execute().fetchall() == [(u1.user_id, u1.name)] </span><span class="cx"> assert user2.select().execute().fetchall() == [(u2.user_id, u2.name)] </span><span class="cx"> assert address1.select().execute().fetchall() == [(u1.user_id, a1.user_id, 'a1...@fo...')] </span><span class="cx"> assert address2.select().execute().fetchall() == [(u2.user_id, a2.user_id, 'a2...@fo...')] </span><span class="cx"> </span><del>- objectstore.clear() - u1list = u1mapper.select() - u2list = u2mapper.select() </del><ins>+ ctx.current.clear() + u1list = ctx.current.query(User, entity_name='user1').select() + u2list = ctx.current.query(User, entity_name='user2').select() </ins><span class="cx"> assert len(u1list) == len(u2list) == 1 </span><span class="cx"> assert u1list[0] is not u2list[0] </span><span class="cx"> assert len(u1list[0].addresses) == len(u2list[0].addresses) == 1 </span><span class="lines">@@ -90,14 +88,14 @@ </span><span class="cx"> class Address1(object):pass </span><span class="cx"> class Address2(object):pass </span><span class="cx"> </span><del>- a1mapper = mapper(Address1, address1) - a2mapper = mapper(Address2, address2) </del><ins>+ a1mapper = mapper(Address1, address1, extension=ctx.mapper_extension) + a2mapper = mapper(Address2, address2, extension=ctx.mapper_extension) </ins><span class="cx"> u1mapper = mapper(User, user1, entity_name='user1', properties ={ </span><span class="cx"> 'addresses':relation(a1mapper) </span><del>- }) </del><ins>+ }, extension=ctx.mapper_extension) </ins><span class="cx"> u2mapper =mapper(User, user2, entity_name='user2', properties={ </span><span class="cx"> 'addresses':relation(a2mapper) </span><del>- }) </del><ins>+ }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><span class="cx"> u1 = User(_sa_entity_name='user1') </span><span class="cx"> u1.name = 'this is user 1' </span><span class="lines">@@ -111,15 +109,15 @@ </span><span class="cx"> a2.email='a2...@fo...' </span><span class="cx"> u2.addresses.append(a2) </span><span class="cx"> </span><del>- objectstore.commit() </del><ins>+ ctx.current.flush() </ins><span class="cx"> assert user1.select().execute().fetchall() == [(u1.user_id, u1.name)] </span><span class="cx"> assert user2.select().execute().fetchall() == [(u2.user_id, u2.name)] </span><span class="cx"> assert address1.select().execute().fetchall() == [(u1.user_id, a1.user_id, 'a1...@fo...')] </span><span class="cx"> assert address2.select().execute().fetchall() == [(u2.user_id, a2.user_id, 'a2...@fo...')] </span><span class="cx"> </span><del>- objectstore.clear() - u1list = u1mapper.select() - u2list = u2mapper.select() </del><ins>+ ctx.current.clear() + u1list = ctx.current.query(User, entity_name='user1').select() + u2list = ctx.current.query(User, entity_name='user2').select() </ins><span class="cx"> assert len(u1list) == len(u2list) == 1 </span><span class="cx"> assert u1list[0] is not u2list[0] </span><span class="cx"> assert len(u1list[0].addresses) == len(u2list[0].addresses) == 1 </span></span></pre></div> <a id="sqlalchemybranchesschematestindexespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/indexes.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/indexes.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/test/indexes.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -101,15 +101,15 @@ </span><span class="cx"> sport='hockey', announcer='some canadian', </span><span class="cx"> winner='sweden') </span><span class="cx"> ss = events.select().execute().fetchall() </span><del>- </del><ins>+ </ins><span class="cx"> assert capt[0].strip().startswith('CREATE TABLE events') </span><del>- assert capt[2].strip() == \ </del><ins>+ assert capt[3].strip() == \ </ins><span class="cx"> 'CREATE UNIQUE INDEX ux_name ON events (name)' </span><del>- assert capt[4].strip() == \ </del><ins>+ assert capt[6].strip() == \ </ins><span class="cx"> 'CREATE INDEX ix_location ON events (location)' </span><del>- assert capt[6].strip() == \ </del><ins>+ assert capt[9].strip() == \ </ins><span class="cx"> 'CREATE UNIQUE INDEX sport_announcer ON events (sport, announcer)' </span><del>- assert capt[8].strip() == \ </del><ins>+ assert capt[12].strip() == \ </ins><span class="cx"> 'CREATE INDEX idx_winners ON events (winner)' </span><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span></span></pre></div> <a id="sqlalchemybranchesschematestlazytest1py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/lazytest1.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/lazytest1.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/test/lazytest1.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -6,28 +6,25 @@ </span><span class="cx"> </span><span class="cx"> class LazyTest(AssertMixin): </span><span class="cx"> def setUpAll(self): </span><del>- global info_table, data_table, rel_table - engine = testbase.db - info_table = Table('infos', engine, </del><ins>+ global info_table, data_table, rel_table, metadata + metadata = BoundMetaData(testbase.db) + info_table = Table('infos', metadata, </ins><span class="cx"> Column('pk', Integer, primary_key=True), </span><span class="cx"> Column('info', String)) </span><span class="cx"> </span><del>- data_table = Table('data', engine, </del><ins>+ data_table = Table('data', metadata, </ins><span class="cx"> Column('data_pk', Integer, primary_key=True), </span><span class="cx"> Column('info_pk', Integer, ForeignKey(info_table.c.pk)), </span><span class="cx"> Column('timeval', Integer), </span><span class="cx"> Column('data_val', String)) </span><span class="cx"> </span><del>- rel_table = Table('rels', engine, </del><ins>+ rel_table = Table('rels', metadata, </ins><span class="cx"> Column('rel_pk', Integer, primary_key=True), </span><span class="cx"> Column('info_pk', Integer, ForeignKey(info_table.c.pk)), </span><span class="cx"> Column('start', Integer), </span><span class="cx"> Column('finish', Integer)) </span><span class="cx"> </span><del>- - info_table.create() - rel_table.create() - data_table.create() </del><ins>+ metadata.create_all() </ins><span class="cx"> info_table.insert().execute( </span><span class="cx"> {'pk':1, 'info':'pk_1_info'}, </span><span class="cx"> {'pk':2, 'info':'pk_2_info'}, </span><span class="lines">@@ -52,9 +49,7 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><del>- data_table.drop() - rel_table.drop() - info_table.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> </span><span class="cx"> def testone(self): </span><span class="cx"> """tests a lazy load which has multiple join conditions, including two that are against </span><span class="lines">@@ -70,19 +65,21 @@ </span><span class="cx"> </span><span class="cx"> session = create_session() </span><span class="cx"> </span><del>- # Create the basic mappers, with no frills or modifications - Information.mapper = mapper(Information, info_table) - Data.mapper = mapper(Data, data_table) - Relation.mapper = mapper(Relation, rel_table) </del><ins>+ mapper(Data, data_table) + mapper(Relation, rel_table, properties={ + + 'datas': relation(Data, + primaryjoin=and_(rel_table.c.info_pk==Data.c.info_pk, + Data.c.timeval >= rel_table.c.start, + Data.c.timeval <= rel_table.c.finish), + foreignkey=Data.c.info_pk) + } + + ) + mapper(Information, info_table, properties={ + 'rels': relation(Relation) + }) </ins><span class="cx"> </span><del>- Relation.mapper.add_property('datas', relation(Data.mapper, - primaryjoin=and_(Relation.c.info_pk==Data.c.info_pk, - Data.c.timeval >= Relation.c.start, - Data.c.timeval <= Relation.c.finish), - foreignkey=Data.c.info_pk)) - - Information.mapper.add_property('rels', relation(Relation.mapper)) - </del><span class="cx"> info = session.query(Information).get(1) </span><span class="cx"> assert info </span><span class="cx"> assert len(info.rels) == 2 </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -3,13 +3,26 @@ </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> import StringIO </span><span class="cx"> import testbase </span><ins>+from sqlalchemy.orm.mapper import global_extensions +from sqlalchemy.ext.sessioncontext import SessionContext </ins><span class="cx"> </span><span class="cx"> from tables import * </span><span class="cx"> import tables </span><span class="cx"> </span><del>-class HistoryTest(AssertMixin): </del><ins>+class SessionTest(AssertMixin): </ins><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() </del><ins>+ global ctx + ctx = SessionContext(create_session) + global_extensions.append(ctx.mapper_extension) + def tearDownAll(self): + global_extensions.remove(ctx.mapper_extension) + def tearDown(self): + ctx.current.clear() + clear_mappers() + +class HistoryTest(SessionTest): + def setUpAll(self): + SessionTest.setUpAll(self) </ins><span class="cx"> db.echo = False </span><span class="cx"> users.create() </span><span class="cx"> addresses.create() </span><span class="lines">@@ -19,10 +32,7 @@ </span><span class="cx"> addresses.drop() </span><span class="cx"> users.drop() </span><span class="cx"> db.echo = testbase.echo </span><del>- self.uninstall_threadlocal() - def setUp(self): - objectstore.clear() - clear_mappers() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> </span><span class="cx"> def testattr(self): </span><span class="cx"> """tests the rolling back of scalar and list attributes. this kind of thing </span><span class="lines">@@ -45,7 +55,7 @@ </span><span class="cx"> self.assert_result([u], data[0], *data[1:]) </span><span class="cx"> </span><span class="cx"> self.echo(repr(u.addresses)) </span><del>- objectstore.get_session().uow.rollback_object(u) </del><ins>+ ctx.current.uow.rollback_object(u) </ins><span class="cx"> data = [User, </span><span class="cx"> {'user_name' : None, </span><span class="cx"> 'addresses' : (Address, []) </span><span class="lines">@@ -62,10 +72,8 @@ </span><span class="cx"> addresses = relation(am, backref='user', lazy=False)) </span><span class="cx"> ) </span><span class="cx"> </span><del>- u = User() - a = Address() - s.save(u) - s.save(a) </del><ins>+ u = User(_sa_session=s) + a = Address(_sa_session=s) </ins><span class="cx"> a.user = u </span><span class="cx"> #print repr(a.__class__._attribute_manager.get_history(a, 'user').added_items()) </span><span class="cx"> #print repr(u.addresses.added_items()) </span><span class="lines">@@ -77,10 +85,10 @@ </span><span class="cx"> print u.addresses[0].user </span><span class="cx"> </span><span class="cx"> </span><del>-class VersioningTest(AssertMixin): </del><ins>+class VersioningTest(SessionTest): </ins><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() - objectstore.clear() </del><ins>+ SessionTest.setUpAll(self) + ctx.current.clear() </ins><span class="cx"> global version_table </span><span class="cx"> version_table = Table('version_test', db, </span><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="lines">@@ -89,11 +97,10 @@ </span><span class="cx"> ).create() </span><span class="cx"> def tearDownAll(self): </span><span class="cx"> version_table.drop() </span><del>- self.uninstall_threadlocal() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> def tearDown(self): </span><span class="cx"> version_table.delete().execute() </span><del>- objectstore.clear() - clear_mappers() </del><ins>+ SessionTest.tearDown(self) </ins><span class="cx"> </span><span class="cx"> @testbase.unsupported('mysql') </span><span class="cx"> def testbasic(self): </span><span class="lines">@@ -138,10 +145,9 @@ </span><span class="cx"> success = True </span><span class="cx"> assert success </span><span class="cx"> </span><del>-class UnicodeTest(AssertMixin): </del><ins>+class UnicodeTest(SessionTest): </ins><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() - objectstore.clear() </del><ins>+ SessionTest.setUpAll(self) </ins><span class="cx"> global uni_table </span><span class="cx"> uni_table = Table('uni_test', db, </span><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="lines">@@ -149,8 +155,7 @@ </span><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><span class="cx"> uni_table.drop() </span><del>- uni_table.deregister() - self.uninstall_threadlocal() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class Test(object): </span><span class="lines">@@ -162,13 +167,13 @@ </span><span class="cx"> txt = u"\u0160\u0110\u0106\u010c\u017d" </span><span class="cx"> t1 = Test(id=1, txt = txt) </span><span class="cx"> self.assert_(t1.txt == txt) </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(t1.txt == txt) </span><span class="cx"> </span><span class="cx"> </span><del>-class PKTest(AssertMixin): </del><ins>+class PKTest(SessionTest): </ins><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() </del><ins>+ SessionTest.setUpAll(self) </ins><span class="cx"> db.echo = False </span><span class="cx"> global table </span><span class="cx"> global table2 </span><span class="lines">@@ -202,10 +207,7 @@ </span><span class="cx"> table2.drop() </span><span class="cx"> table3.drop() </span><span class="cx"> db.echo = testbase.echo </span><del>- self.uninstall_threadlocal() - def setUp(self): - objectstore.clear() - clear_mappers() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> </span><span class="cx"> @testbase.unsupported('sqlite') </span><span class="cx"> def testprimarykey(self): </span><span class="lines">@@ -216,8 +218,8 @@ </span><span class="cx"> e.name = 'entry1' </span><span class="cx"> e.value = 'this is entry 1' </span><span class="cx"> e.multi_rev = 2 </span><del>- objectstore.flush() - objectstore.clear() </del><ins>+ ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> e2 = Entry.mapper.get((e.multi_id, 2)) </span><span class="cx"> self.assert_(e is not e2 and e._instance_key == e2._instance_key) </span><span class="cx"> def testmanualpk(self): </span><span class="lines">@@ -228,7 +230,7 @@ </span><span class="cx"> e.pk_col_1 = 'pk1' </span><span class="cx"> e.pk_col_2 = 'pk1_related' </span><span class="cx"> e.data = 'im the data' </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> def testkeypks(self): </span><span class="cx"> import datetime </span><span class="cx"> class Entity(object): </span><span class="lines">@@ -239,12 +241,12 @@ </span><span class="cx"> e.secondary = 'pk2' </span><span class="cx"> e.assigned = datetime.date.today() </span><span class="cx"> e.data = 'some more data' </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><del>-class PrivateAttrTest(AssertMixin): </del><ins>+class PrivateAttrTest(SessionTest): </ins><span class="cx"> """tests various things to do with private=True mappers""" </span><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() </del><ins>+ SessionTest.setUpAll(self) </ins><span class="cx"> global a_table, b_table </span><span class="cx"> a_table = Table('a',testbase.db, </span><span class="cx"> Column('a_id', Integer, Sequence('next_a_id'), primary_key=True), </span><span class="lines">@@ -258,10 +260,7 @@ </span><span class="cx"> def tearDownAll(self): </span><span class="cx"> b_table.drop() </span><span class="cx"> a_table.drop() </span><del>- self.uninstall_threadlocal() - def setUp(self): - objectstore.clear() - clear_mappers() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> </span><span class="cx"> def testsinglecommit(self): </span><span class="cx"> """tests that a commit of a single object deletes private relationships""" </span><span class="lines">@@ -284,10 +283,10 @@ </span><span class="cx"> a.bs.append(b2) </span><span class="cx"> </span><span class="cx"> # inserts both A and Bs </span><del>- objectstore.flush([a]) </del><ins>+ ctx.current.flush([a]) </ins><span class="cx"> </span><del>- objectstore.delete(a) - objectstore.flush([a]) </del><ins>+ ctx.current.delete(a) + ctx.current.flush([a]) </ins><span class="cx"> </span><span class="cx"> assert b_table.count().scalar() == 0 </span><span class="cx"> </span><span class="lines">@@ -304,24 +303,24 @@ </span><span class="cx"> a2 = A(data='testa2') </span><span class="cx"> b = B(data='testb') </span><span class="cx"> b.a = a1 </span><del>- objectstore.flush() - objectstore.clear() - sess = objectstore.get_session() </del><ins>+ ctx.current.flush() + ctx.current.clear() + sess = ctx.current </ins><span class="cx"> a1 = A.mapper.get(a1.a_id) </span><span class="cx"> a2 = A.mapper.get(a2.a_id) </span><span class="cx"> assert a1.bs[0].a is a1 </span><span class="cx"> b = a1.bs[0] </span><span class="cx"> b.a = a2 </span><span class="cx"> assert b not in sess.deleted </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> assert b in sess.identity_map.values() </span><span class="cx"> </span><del>-class DefaultTest(AssertMixin): </del><ins>+class DefaultTest(SessionTest): </ins><span class="cx"> """tests that when saving objects whose table contains DefaultGenerators, either python-side, preexec or database-side, </span><span class="cx"> the newly saved instances receive all the default values either through a post-fetch or getting the pre-exec'ed </span><span class="cx"> defaults back from the engine.""" </span><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() </del><ins>+ SessionTest.setUpAll(self) </ins><span class="cx"> #db.echo = 'debug' </span><span class="cx"> use_string_defaults = db.engine.__module__.endswith('postgres') or db.engine.__module__.endswith('oracle') or db.engine.__module__.endswith('sqlite') </span><span class="cx"> </span><span class="lines">@@ -342,7 +341,7 @@ </span><span class="cx"> self.table.create() </span><span class="cx"> def tearDownAll(self): </span><span class="cx"> self.table.drop() </span><del>- self.uninstall_threadlocal() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> def setUp(self): </span><span class="cx"> self.table = Table('default_test', db) </span><span class="cx"> def testinsert(self): </span><span class="lines">@@ -353,7 +352,7 @@ </span><span class="cx"> h3 = Hoho(hoho=self.althohoval, counter=12) </span><span class="cx"> h4 = Hoho() </span><span class="cx"> h5 = Hoho(foober='im the new foober') </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(h1.hoho==self.althohoval) </span><span class="cx"> self.assert_(h3.hoho==self.althohoval) </span><span class="cx"> self.assert_(h2.hoho==h4.hoho==h5.hoho==self.hohoval) </span><span class="lines">@@ -361,7 +360,7 @@ </span><span class="cx"> self.assert_(h1.counter == h4.counter==h5.counter==7) </span><span class="cx"> self.assert_(h2.foober == h3.foober == h4.foober == 'im foober') </span><span class="cx"> self.assert_(h5.foober=='im the new foober') </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> l = Hoho.mapper.select() </span><span class="cx"> (h1, h2, h3, h4, h5) = l </span><span class="cx"> self.assert_(h1.hoho==self.althohoval) </span><span class="lines">@@ -377,7 +376,7 @@ </span><span class="cx"> class Hoho(object):pass </span><span class="cx"> assign_mapper(Hoho, self.table) </span><span class="cx"> h1 = Hoho(hoho="15", counter="15") </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(h1.hoho=="15") </span><span class="cx"> self.assert_(h1.counter=="15") </span><span class="cx"> self.assert_(h1.foober=="im foober") </span><span class="lines">@@ -386,16 +385,16 @@ </span><span class="cx"> class Hoho(object):pass </span><span class="cx"> assign_mapper(Hoho, self.table) </span><span class="cx"> h1 = Hoho() </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(h1.foober == 'im foober') </span><span class="cx"> h1.counter = 19 </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(h1.foober == 'im the update') </span><span class="cx"> </span><del>-class SaveTest(AssertMixin): </del><ins>+class SaveTest(SessionTest): </ins><span class="cx"> </span><span class="cx"> def setUpAll(self): </span><del>- self.install_threadlocal() </del><ins>+ SessionTest.setUpAll(self) </ins><span class="cx"> db.echo = False </span><span class="cx"> tables.create() </span><span class="cx"> db.echo = testbase.echo </span><span class="lines">@@ -403,14 +402,10 @@ </span><span class="cx"> db.echo = False </span><span class="cx"> tables.drop() </span><span class="cx"> db.echo = testbase.echo </span><del>- self.uninstall_threadlocal() </del><ins>+ SessionTest.tearDownAll(self) </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><span class="cx"> db.echo = False </span><del>- # remove all history/identity maps etc. - objectstore.clear() - # remove all mapperes - clear_mappers() </del><span class="cx"> keywords.insert().execute( </span><span class="cx"> dict(name='blue'), </span><span class="cx"> dict(name='red'), </span><span class="lines">@@ -427,9 +422,10 @@ </span><span class="cx"> tables.delete() </span><span class="cx"> db.echo = testbase.echo </span><span class="cx"> </span><del>- self.assert_(len(objectstore.get_session().new) == 0) - self.assert_(len(objectstore.get_session().dirty) == 0) - </del><ins>+ self.assert_(len(ctx.current.new) == 0) + self.assert_(len(ctx.current.dirty) == 0) + SessionTest.tearDown(self) + </ins><span class="cx"> def testbasic(self): </span><span class="cx"> # save two users </span><span class="cx"> u = User() </span><span class="lines">@@ -438,10 +434,10 @@ </span><span class="cx"> u2 = User() </span><span class="cx"> u2.user_name = 'savetester2' </span><span class="cx"> </span><del>- objectstore.get_session().save(u) </del><ins>+ ctx.current.save(u) </ins><span class="cx"> </span><del>- objectstore.get_session().flush([u]) - objectstore.get_session().flush() </del><ins>+ ctx.current.flush([u]) + ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> # assert the first one retreives the same from the identity map </span><span class="cx"> nu = m.get(u.user_id) </span><span class="lines">@@ -449,20 +445,20 @@ </span><span class="cx"> self.assert_(u is nu) </span><span class="cx"> </span><span class="cx"> # clear out the identity map, so next get forces a SELECT </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><span class="cx"> # check it again, identity should be different but ids the same </span><span class="cx"> nu = m.get(u.user_id) </span><span class="cx"> self.assert_(u is not nu and u.user_id == nu.user_id and nu.user_name == 'savetester') </span><span class="cx"> </span><span class="cx"> # change first users name and save </span><del>- objectstore.get_session().update(u) </del><ins>+ ctx.current.update(u) </ins><span class="cx"> u.user_name = 'modifiedname' </span><del>- assert u in objectstore.get_session().dirty - objectstore.get_session().flush() </del><ins>+ assert u in ctx.current.dirty + ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> # select both </span><del>- #objectstore.clear() </del><ins>+ #ctx.current.clear() </ins><span class="cx"> userlist = m.select(users.c.user_id.in_(u.user_id, u2.user_id), order_by=[users.c.user_name]) </span><span class="cx"> print repr(u.user_id), repr(userlist[0].user_id), repr(userlist[0].user_name) </span><span class="cx"> self.assert_(u.user_id == userlist[0].user_id and userlist[0].user_name == 'modifiedname') </span><span class="lines">@@ -480,12 +476,12 @@ </span><span class="cx"> u.addresses.append(Address()) </span><span class="cx"> u.addresses.append(Address()) </span><span class="cx"> u.addresses.append(Address()) </span><del>- objectstore.flush() - objectstore.clear() </del><ins>+ ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> ulist = m1.select() </span><span class="cx"> u1 = ulist[0] </span><span class="cx"> u1.user_name = 'newname' </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.assert_(len(u1.addresses) == 4) </span><span class="cx"> </span><span class="cx"> def testinherits(self): </span><span class="lines">@@ -502,8 +498,8 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> au = AddressUser() </span><del>- objectstore.flush() - objectstore.clear() </del><ins>+ ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> l = AddressUser.mapper.selectone() </span><span class="cx"> self.assert_(l.user_id == au.user_id and l.address_id == au.address_id) </span><span class="cx"> </span><span class="lines">@@ -524,7 +520,7 @@ </span><span class="cx"> u.user_name = 'multitester' </span><span class="cx"> u.email = 'mu...@te...' </span><span class="cx"> </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> usertable = users.select(users.c.user_id.in_(u.foo_id)).execute().fetchall() </span><span class="cx"> self.assertEqual(usertable[0].values(), [u.foo_id, 'multitester']) </span><span class="lines">@@ -533,7 +529,7 @@ </span><span class="cx"> </span><span class="cx"> u.email = 'la...@he...' </span><span class="cx"> u.user_name = 'imnew' </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> usertable = users.select(users.c.user_id.in_(u.foo_id)).execute().fetchall() </span><span class="cx"> self.assertEqual(usertable[0].values(), [u.foo_id, 'imnew']) </span><span class="lines">@@ -551,11 +547,11 @@ </span><span class="cx"> u.user_name = 'one2onetester' </span><span class="cx"> u.address = Address() </span><span class="cx"> u.address.email_address = 'myo...@fo...' </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> u.user_name = 'imnew' </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> u.address.email_address = 'im...@fo...' </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> def testchildmove(self): </span><span class="cx"> """tests moving a child from one parent to the other, then deleting the first parent, properly </span><span class="lines">@@ -570,12 +566,12 @@ </span><span class="cx"> a = Address() </span><span class="cx"> a.email_address = 'address1' </span><span class="cx"> u1.addresses.append(a) </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> del u1.addresses[0] </span><span class="cx"> u2.addresses.append(a) </span><del>- objectstore.delete(u1) - objectstore.flush() - objectstore.clear() </del><ins>+ ctx.current.delete(u1) + ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> u2 = m.get(u2.user_id) </span><span class="cx"> assert len(u2.addresses) == 1 </span><span class="cx"> </span><span class="lines">@@ -588,11 +584,11 @@ </span><span class="cx"> u.user_name = 'one2onetester' </span><span class="cx"> u.address = a </span><span class="cx"> u.address.email_address = 'myo...@fo...' </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> self.echo("\n\n\n") </span><del>- objectstore.get_session().delete(u) - objectstore.get_session().flush() - self.assert_(a.address_id is not None and a.user_id is None and not objectstore.get_session().identity_map.has_key(u._instance_key) and objectstore.get_session().identity_map.has_key(a._instance_key)) </del><ins>+ ctx.current.delete(u) + ctx.current.flush() + self.assert_(a.address_id is not None and a.user_id is None and not ctx.current.identity_map.has_key(u._instance_key) and ctx.current.identity_map.has_key(a._instance_key)) </ins><span class="cx"> </span><span class="cx"> def testcascadingdelete(self): </span><span class="cx"> m = mapper(User, users, properties = dict( </span><span class="lines">@@ -645,8 +641,8 @@ </span><span class="cx"> i.item_name = item['item_name'] </span><span class="cx"> o.items.append(i) </span><span class="cx"> </span><del>- objectstore.get_session().flush() - objectstore.clear() </del><ins>+ ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> </span><span class="cx"> l = m.select() </span><span class="cx"> for u in l: </span><span class="lines">@@ -654,10 +650,10 @@ </span><span class="cx"> self.assert_result(l, data[0], *data[1:]) </span><span class="cx"> </span><span class="cx"> self.echo("\n\n\n") </span><del>- objectstore.get_session().delete(l[0], l[2]) - objectstore.flush() </del><ins>+ ctx.current.delete(l[0], l[2]) + ctx.current.flush() </ins><span class="cx"> return </span><del>- res = self.capture_exec(db, lambda: objectstore.get_session().flush()) </del><ins>+ res = self.capture_exec(db, lambda: ctx.current.flush()) </ins><span class="cx"> state = None </span><span class="cx"> </span><span class="cx"> for line in res.split('\n'): </span><span class="lines">@@ -695,11 +691,11 @@ </span><span class="cx"> a.user.user_name = elem['user_name'] </span><span class="cx"> objects.append(a) </span><span class="cx"> </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> objects[2].email_address = 'im...@fo...r' </span><span class="cx"> objects[3].user = User() </span><span class="cx"> objects[3].user.user_name = 'imnewlyadded' </span><del>- self.assert_sql(db, lambda: objectstore.get_session().flush(), [ </del><ins>+ self.assert_sql(db, lambda: ctx.current.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "INSERT INTO users (user_name) VALUES (:user_name)", </span><span class="cx"> {'user_name': 'imnewlyadded'} </span><span class="lines">@@ -750,7 +746,7 @@ </span><span class="cx"> u.addresses.append(a2) </span><span class="cx"> self.echo( repr(u.addresses)) </span><span class="cx"> self.echo( repr(u.addresses.added_items())) </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> usertable = users.select(users.c.user_id.in_(u.user_id)).execute().fetchall() </span><span class="cx"> self.assertEqual(usertable[0].values(), [u.user_id, 'one2manytester']) </span><span class="lines">@@ -763,7 +759,7 @@ </span><span class="cx"> </span><span class="cx"> a2.email_address = 'som...@fo...' </span><span class="cx"> </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> addresstable = addresses.select(addresses.c.address_id == addressid).execute().fetchall() </span><span class="lines">@@ -792,7 +788,7 @@ </span><span class="cx"> u[0].addresses[0].email_address='hi' </span><span class="cx"> </span><span class="cx"> # insure that upon commit, the new mapper with the address relation is used </span><del>- self.assert_sql(db, lambda: objectstore.flush(), </del><ins>+ self.assert_sql(db, lambda: ctx.current.flush(), </ins><span class="cx"> [ </span><span class="cx"> ( </span><span class="cx"> "INSERT INTO email_addresses (user_id, email_address) VALUES (:user_id, :email_address)", </span><span class="lines">@@ -829,7 +825,7 @@ </span><span class="cx"> a3 = Address() </span><span class="cx"> a3.email_address = 'emailaddress3' </span><span class="cx"> </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> self.echo("\n\n\n") </span><span class="cx"> # modify user2 directly, append an address to user1. </span><span class="lines">@@ -838,7 +834,7 @@ </span><span class="cx"> u2.user_name = 'user2modified' </span><span class="cx"> u1.addresses.append(a3) </span><span class="cx"> del u1.addresses[0] </span><del>- self.assert_sql(db, lambda: objectstore.flush(), </del><ins>+ self.assert_sql(db, lambda: ctx.current.flush(), </ins><span class="cx"> [ </span><span class="cx"> ( </span><span class="cx"> "UPDATE users SET user_name=:user_name WHERE users.user_id = :users_user_id", </span><span class="lines">@@ -863,12 +859,12 @@ </span><span class="cx"> u1.user_name='user1' </span><span class="cx"> </span><span class="cx"> a1.user = u1 </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> self.echo("\n\n\n") </span><del>- objectstore.delete(u1) </del><ins>+ ctx.current.delete(u1) </ins><span class="cx"> a1.user = None </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> def _testalias(self): </span><span class="cx"> """tests that an alias of a table can be used in a mapper. </span><span class="lines">@@ -947,7 +943,7 @@ </span><span class="cx"> k.name = kname </span><span class="cx"> item.keywords.append(k) </span><span class="cx"> </span><del>- objectstore.get_session().flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> l = m.select(items.c.item_name.in_(*[e['item_name'] for e in data[1:]]), order_by=[items.c.item_name, keywords.c.name]) </span><span class="cx"> self.assert_result(l, *data) </span><span class="lines">@@ -956,7 +952,7 @@ </span><span class="cx"> k = Keyword() </span><span class="cx"> k.name = 'yellow' </span><span class="cx"> objects[5].keywords.append(k) </span><del>- self.assert_sql(db, lambda:objectstore.flush(), [ </del><ins>+ self.assert_sql(db, lambda:ctx.current.flush(), [ </ins><span class="cx"> { </span><span class="cx"> "UPDATE items SET item_name=:item_name WHERE items.item_id = :items_item_id": </span><span class="cx"> {'item_name': 'item4updated', 'items_item_id': objects[4].item_id} </span><span class="lines">@@ -985,7 +981,7 @@ </span><span class="cx"> objects[2].keywords.append(k) </span><span class="cx"> dkid = objects[5].keywords[1].keyword_id </span><span class="cx"> del objects[5].keywords[1] </span><del>- self.assert_sql(db, lambda:objectstore.flush(), [ </del><ins>+ self.assert_sql(db, lambda:ctx.current.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "DELETE FROM itemkeywords WHERE itemkeywords.item_id = :item_id AND itemkeywords.keyword_id = :keyword_id", </span><span class="cx"> [{'item_id': objects[5].item_id, 'keyword_id': dkid}] </span><span class="lines">@@ -996,8 +992,8 @@ </span><span class="cx"> ) </span><span class="cx"> ]) </span><span class="cx"> </span><del>- objectstore.delete(objects[3]) - objectstore.flush() </del><ins>+ ctx.current.delete(objects[3]) + ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> def testassociation(self): </span><span class="cx"> class IKAssociation(object): </span><span class="lines">@@ -1057,8 +1053,8 @@ </span><span class="cx"> ik.keyword = k </span><span class="cx"> item.keywords.append(ik) </span><span class="cx"> </span><del>- objectstore.get_session().flush() - objectstore.clear() </del><ins>+ ctx.current.flush() + ctx.current.clear() </ins><span class="cx"> l = m.select(items.c.item_name.in_(*[e['item_name'] for e in data[1:]]), order_by=[items.c.item_name, keywords.c.name]) </span><span class="cx"> self.assert_result(l, *data) </span><span class="cx"> </span><span class="lines">@@ -1076,7 +1072,7 @@ </span><span class="cx"> a = Address() </span><span class="cx"> a.email_address = 'testaddress' </span><span class="cx"> a.user = u </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> print repr(u.addresses) </span><span class="cx"> x = False </span><span class="cx"> try: </span><span class="lines">@@ -1088,8 +1084,8 @@ </span><span class="cx"> if x: </span><span class="cx"> self.assert_(False, "User addresses element should be scalar based") </span><span class="cx"> </span><del>- objectstore.delete(u) - objectstore.flush() </del><ins>+ ctx.current.delete(u) + ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> def testdoublerelation(self): </span><span class="cx"> m2 = mapper(Address, addresses) </span><span class="lines">@@ -1109,14 +1105,13 @@ </span><span class="cx"> </span><span class="cx"> u.boston_addresses.append(a) </span><span class="cx"> u.newyork_addresses.append(b) </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><del>-class SaveTest2(AssertMixin): </del><ins>+class SaveTest2(SessionTest): </ins><span class="cx"> </span><span class="cx"> def setUp(self): </span><del>- self.install_threadlocal() </del><span class="cx"> db.echo = False </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> clear_mappers() </span><span class="cx"> self.users = Table('users', db, </span><span class="cx"> Column('user_id', Integer, Sequence('user_id_seq', optional=True), primary_key = True), </span><span class="lines">@@ -1142,7 +1137,7 @@ </span><span class="cx"> self.addresses.drop() </span><span class="cx"> self.users.drop() </span><span class="cx"> db.echo = testbase.echo </span><del>- self.uninstall_threadlocal() </del><ins>+ SessionTest.tearDown(self) </ins><span class="cx"> </span><span class="cx"> def testbackwardsnonmatch(self): </span><span class="cx"> m = mapper(Address, self.addresses, properties = dict( </span><span class="lines">@@ -1159,7 +1154,7 @@ </span><span class="cx"> a.user = User() </span><span class="cx"> a.user.user_name = elem['user_name'] </span><span class="cx"> objects.append(a) </span><del>- self.assert_sql(db, lambda: objectstore.flush(), [ </del><ins>+ self.assert_sql(db, lambda: ctx.current.flush(), [ </ins><span class="cx"> ( </span><span class="cx"> "INSERT INTO users (user_name) VALUES (:user_name)", </span><span class="cx"> {'user_name': 'thesub'} </span></span></pre></div> <a id="sqlalchemybranchesschematestproxy_enginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/proxy_engine.py (1477 => 1478)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/proxy_engine.py 2006-05-20 18:52:13 UTC (rev 1477) +++ sqlalchemy/branches/schema/test/proxy_engine.py 2006-05-21 16:48:14 UTC (rev 1478) </span><span class="lines">@@ -12,22 +12,28 @@ </span><span class="cx"> # </span><span class="cx"> </span><span class="cx"> </span><del>-module_engine = ProxyEngine(echo=testbase.echo) -module_metadata = MetaData() </del><ins>+class ProxyTestBase(PersistTest): + def setUpAll(self): </ins><span class="cx"> </span><del>-users = Table('users', module_metadata, - Column('user_id', Integer, primary_key=True), - Column('user_name', String(16)), - Column('password', String(20)) - ) </del><ins>+ global users, User, module_engine, module_metadata </ins><span class="cx"> </span><del>-class User(object): - pass </del><ins>+ module_engine = ProxyEngine(echo=testbase.echo) + module_metadata = MetaData() </ins><span class="cx"> </span><del>-User.mapper = mapper(User, users) </del><ins>+ users = Table('users', module_metadata, + Column('user_id', Integer, primary_key=True), + Column('user_name', String(16)), + Column('password', String(20)) + ) </ins><span class="cx"> </span><ins>+ class User(object): + pass </ins><span class="cx"> </span><del>-class ConstructTest(PersistTest): </del><ins>+ User.mapper = mapper(User, users) + def tearDownAll(self): + clear_mappers() + +class ConstructTest(ProxyTestBase): </ins><span class="cx"> """tests that we can build SQL constructs without engine-specific parameters, particulary </span><span class="cx"> oid_column, being needed, as the proxy engine is usually not connected yet.""" </span><span class="cx"> </span><span class="lines">@@ -40,7 +46,7 @@ </span><span class="cx"> j = join(t, t2) </span><span class="cx"> ... [truncated message content] |
From: <co...@sq...> - 2006-05-20 18:52:26
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1477] sqlalchemy/branches/schema/test: got onetoone test working....</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1477</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-20 13:52:13 -0500 (Sat, 20 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>got onetoone test working....</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyattributespy">sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py</a></li> <li><a href="#sqlalchemybranchesschematestonetoonepy">sqlalchemy/branches/schema/test/onetoone.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py (1476 => 1477)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-20 18:43:07 UTC (rev 1476) +++ sqlalchemy/branches/schema/lib/sqlalchemy/attributes.py 2006-05-20 18:52:13 UTC (rev 1477) </span><span class="lines">@@ -96,8 +96,9 @@ </span><span class="cx"> pass </span><span class="cx"> def hasparent(self, item): </span><span class="cx"> return item.__class__._attribute_manager.attribute_history(item).get('_hasparent_' + self.key) </span><del>- def sethasparent(self, item, value): - item.__class__._attribute_manager.attribute_history(item)['_hasparent_' + self.key] = value </del><ins>+ def sethasparent(self, item, value): + if item is not None: + item.__class__._attribute_manager.attribute_history(item)['_hasparent_' + self.key] = value </ins><span class="cx"> </span><span class="cx"> class ScalarAttribute(ManagedAttribute): </span><span class="cx"> """Used by AttributeManager to track the history of a scalar attribute </span></span></pre></div> <a id="sqlalchemybranchesschematestonetoonepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/onetoone.py (1476 => 1477)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/onetoone.py 2006-05-20 18:43:07 UTC (rev 1476) +++ sqlalchemy/branches/schema/test/onetoone.py 2006-05-20 18:52:13 UTC (rev 1477) </span><span class="lines">@@ -1,5 +1,6 @@ </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> import testbase </span><ins>+from sqlalchemy.ext.sessioncontext import SessionContext </ins><span class="cx"> </span><span class="cx"> class Jack(object): </span><span class="cx"> def __repr__(self): </span><span class="lines">@@ -23,8 +24,10 @@ </span><span class="cx"> </span><span class="cx"> class O2OTest(testbase.AssertMixin): </span><span class="cx"> def setUpAll(self): </span><del>- global jack, port - jack = Table('jack', testbase.db, </del><ins>+ global jack, port, metadata, ctx + metadata = BoundMetaData(testbase.db) + ctx = SessionContext(create_session) + jack = Table('jack', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> #Column('room_id', Integer, ForeignKey("room.id")), </span><span class="cx"> Column('number', String(50)), </span><span class="lines">@@ -33,54 +36,54 @@ </span><span class="cx"> ) </span><span class="cx"> </span><span class="cx"> </span><del>- port = Table('port', testbase.db, </del><ins>+ port = Table('port', metadata, </ins><span class="cx"> Column('id', Integer, primary_key=True), </span><span class="cx"> #Column('device_id', Integer, ForeignKey("device.id")), </span><span class="cx"> Column('name', String(30)), </span><span class="cx"> Column('description', String(100)), </span><span class="cx"> Column('jack_id', Integer, ForeignKey("jack.id")), </span><span class="cx"> ) </span><del>- jack.create() - port.create() </del><ins>+ metadata.create_all() </ins><span class="cx"> def setUp(self): </span><del>- objectstore.clear() </del><ins>+ pass </ins><span class="cx"> def tearDown(self): </span><span class="cx"> clear_mappers() </span><span class="cx"> def tearDownAll(self): </span><del>- port.drop() - jack.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> </span><span class="cx"> def test1(self): </span><del>- assign_mapper(Port, port) - assign_mapper(Jack, jack, order_by=[jack.c.number],properties = { - 'port': relation(Port.mapper, backref='jack', uselist=False, lazy=True), - }) </del><ins>+ mapper(Port, port, extension=ctx.mapper_extension) + mapper(Jack, jack, order_by=[jack.c.number],properties = { + 'port': relation(Port, backref='jack', uselist=False, lazy=True), + }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><span class="cx"> j=Jack(number='101') </span><span class="cx"> p=Port(name='fa0/1') </span><span class="cx"> j.port=p </span><del>- objectstore.commit() </del><ins>+ ctx.current.flush() </ins><span class="cx"> jid = j.id </span><span class="cx"> pid = p.id </span><span class="cx"> </span><del>- j=Jack.get(jid) - p=Port.get(pid) </del><ins>+ j=ctx.current.query(Jack).get(jid) + p=ctx.current.query(Port).get(pid) </ins><span class="cx"> print p.jack </span><del>- print j.port </del><ins>+ assert p.jack is not None + assert p.jack is j + assert j.port is not None </ins><span class="cx"> p.jack=None </span><span class="cx"> assert j.port is None #works </span><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- j=Jack.get(jid) - p=Port.get(pid) </del><ins>+ j=ctx.current.query(Jack).get(jid) + p=ctx.current.query(Port).get(pid) </ins><span class="cx"> </span><span class="cx"> j.port=None </span><span class="cx"> self.assert_(p.jack is None) </span><del>- objectstore.commit() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><del>- j.delete() - objectstore.commit() </del><ins>+ ctx.current.delete(j) + ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-20 18:43:16
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1476] sqlalchemy/branches/schema/test/eagertest2.py: 0.2-converted</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1476</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-20 13:43:07 -0500 (Sat, 20 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.2-converted</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematesteagertest2py">sqlalchemy/branches/schema/test/eagertest2.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschematesteagertest2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/eagertest2.py (1475 => 1476)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/eagertest2.py 2006-05-20 18:34:29 UTC (rev 1475) +++ sqlalchemy/branches/schema/test/eagertest2.py 2006-05-20 18:43:07 UTC (rev 1476) </span><span class="lines">@@ -3,17 +3,15 @@ </span><span class="cx"> import unittest, sys, os </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> import datetime </span><ins>+from sqlalchemy.ext.sessioncontext import SessionContext </ins><span class="cx"> </span><del>-metadata = testbase.metadata - </del><span class="cx"> class EagerTest(AssertMixin): </span><span class="cx"> def setUpAll(self): </span><del>- objectstore.clear() - clear_mappers() - testbase.metadata.tables.clear() - - global companies_table, addresses_table, invoice_table, phones_table, items_table </del><ins>+ global companies_table, addresses_table, invoice_table, phones_table, items_table, ctx, metadata </ins><span class="cx"> </span><ins>+ metadata = BoundMetaData(testbase.db) + ctx = SessionContext(create_session) + </ins><span class="cx"> companies_table = Table('companies', metadata, </span><span class="cx"> Column('company_id', Integer, Sequence('company_id_seq', optional=True), primary_key = True), </span><span class="cx"> Column('company_name', String(40)), </span><span class="lines">@@ -46,27 +44,15 @@ </span><span class="cx"> Column('qty', Integer), </span><span class="cx"> ) </span><span class="cx"> </span><del>- companies_table.create() - addresses_table.create() - phones_table.create() - invoice_table.create() - items_table.create() </del><ins>+ metadata.create_all() </ins><span class="cx"> </span><span class="cx"> def tearDownAll(self): </span><del>- items_table.drop() - invoice_table.drop() - phones_table.drop() - addresses_table.drop() - companies_table.drop() </del><ins>+ metadata.drop_all() </ins><span class="cx"> </span><span class="cx"> def tearDown(self): </span><del>- objectstore.clear() </del><span class="cx"> clear_mappers() </span><del>- items_table.delete().execute() - invoice_table.delete().execute() - phones_table.delete().execute() - addresses_table.delete().execute() - companies_table.delete().execute() </del><ins>+ for t in metadata.table_iterator(reverse=True): + t.delete().execute() </ins><span class="cx"> </span><span class="cx"> def testone(self): </span><span class="cx"> """tests eager load of a many-to-one attached to a one-to-many. this testcase illustrated </span><span class="lines">@@ -88,14 +74,14 @@ </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "Invoice:" + repr(getattr(self, 'invoice_id', None)) + " " + repr(getattr(self, 'date', None)) + " " + repr(self.company) </span><span class="cx"> </span><del>- Address.mapper = mapper(Address, addresses_table, properties={ - }) - Company.mapper = mapper(Company, companies_table, properties={ - 'addresses' : relation(Address.mapper, lazy=False), - }) - Invoice.mapper = mapper(Invoice, invoice_table, properties={ - 'company': relation(Company.mapper, lazy=False, ) - }) </del><ins>+ mapper(Address, addresses_table, properties={ + }, extension=ctx.mapper_extension) + mapper(Company, companies_table, properties={ + 'addresses' : relation(Address, lazy=False), + }, extension=ctx.mapper_extension) + mapper(Invoice, invoice_table, properties={ + 'company': relation(Company, lazy=False, ) + }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><span class="cx"> c1 = Company() </span><span class="cx"> c1.company_name = 'company 1' </span><span class="lines">@@ -109,19 +95,18 @@ </span><span class="cx"> i1.date = datetime.datetime.now() </span><span class="cx"> i1.company = c1 </span><span class="cx"> </span><del>- - objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> company_id = c1.company_id </span><span class="cx"> invoice_id = i1.invoice_id </span><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- c = Company.mapper.get(company_id) </del><ins>+ c = ctx.current.query(Company).get(company_id) </ins><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- i = Invoice.mapper.get(invoice_id) </del><ins>+ i = ctx.current.query(Invoice).get(invoice_id) </ins><span class="cx"> </span><span class="cx"> self.echo(repr(c)) </span><span class="cx"> self.echo(repr(i.company)) </span><span class="lines">@@ -153,24 +138,24 @@ </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "Item: " + repr(getattr(self, 'item_id', None)) + " " + repr(getattr(self, 'invoice_id', None)) + " " + repr(self.code) + " " + repr(self.qty) </span><span class="cx"> </span><del>- Phone.mapper = mapper(Phone, phones_table, is_primary=True) </del><ins>+ mapper(Phone, phones_table, extension=ctx.mapper_extension) </ins><span class="cx"> </span><del>- Address.mapper = mapper(Address, addresses_table, properties={ - 'phones': relation(Phone.mapper, lazy=False, backref='address') - }) </del><ins>+ mapper(Address, addresses_table, properties={ + 'phones': relation(Phone, lazy=False, backref='address') + }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><del>- Company.mapper = mapper(Company, companies_table, properties={ - 'addresses' : relation(Address.mapper, lazy=False, backref='company'), - }) </del><ins>+ mapper(Company, companies_table, properties={ + 'addresses' : relation(Address, lazy=False, backref='company'), + }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><del>- Item.mapper = mapper(Item, items_table, is_primary=True) </del><ins>+ mapper(Item, items_table, extension=ctx.mapper_extension) </ins><span class="cx"> </span><del>- Invoice.mapper = mapper(Invoice, invoice_table, properties={ - 'items': relation(Item.mapper, lazy=False, backref='invoice'), - 'company': relation(Company.mapper, lazy=False, backref='invoices') - }) </del><ins>+ mapper(Invoice, invoice_table, properties={ + 'items': relation(Item, lazy=False, backref='invoice'), + 'company': relation(Company, lazy=False, backref='invoices') + }, extension=ctx.mapper_extension) </ins><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> c1 = Company() </span><span class="cx"> c1.company_name = 'company 1' </span><span class="cx"> </span><span class="lines">@@ -205,13 +190,13 @@ </span><span class="cx"> </span><span class="cx"> c1.addresses.append(a2) </span><span class="cx"> </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> company_id = c1.company_id </span><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- a = Company.mapper.get(company_id) </del><ins>+ a = ctx.current.query(Company).get(company_id) </ins><span class="cx"> self.echo(repr(a)) </span><span class="cx"> </span><span class="cx"> # set up an invoice </span><span class="lines">@@ -234,18 +219,18 @@ </span><span class="cx"> item3.qty = 3 </span><span class="cx"> item3.invoice = i1 </span><span class="cx"> </span><del>- objectstore.flush() </del><ins>+ ctx.current.flush() </ins><span class="cx"> </span><span class="cx"> invoice_id = i1.invoice_id </span><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- c = Company.mapper.get(company_id) </del><ins>+ c = ctx.current.query(Company).get(company_id) </ins><span class="cx"> self.echo(repr(c)) </span><span class="cx"> </span><del>- objectstore.clear() </del><ins>+ ctx.current.clear() </ins><span class="cx"> </span><del>- i = Invoice.mapper.get(invoice_id) </del><ins>+ i = ctx.current.query(Invoice).get(invoice_id) </ins><span class="cx"> self.echo(repr(i)) </span><span class="cx"> </span><span class="cx"> self.assert_(repr(i.company) == repr(c)) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-20 18:34:38
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1475] sqlalchemy/branches/schema/test/polymorph.py: id fix</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1475</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-20 13:34:29 -0500 (Sat, 20 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>id fix</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestpolymorphpy">sqlalchemy/branches/schema/test/polymorph.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschematestpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/polymorph.py (1474 => 1475)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/polymorph.py 2006-05-20 17:45:06 UTC (rev 1474) +++ sqlalchemy/branches/schema/test/polymorph.py 2006-05-20 18:34:29 UTC (rev 1475) </span><span class="lines">@@ -137,8 +137,8 @@ </span><span class="cx"> print session.new </span><span class="cx"> session.flush() </span><span class="cx"> session.clear() </span><del>- - c = session.query(Company).get(1) </del><ins>+ id = c.company_id + c = session.query(Company).get(id) </ins><span class="cx"> for e in c.employees: </span><span class="cx"> print e, e._instance_key, e.company </span><span class="cx"> if include_base: </span><span class="lines">@@ -157,7 +157,7 @@ </span><span class="cx"> session.flush() </span><span class="cx"> session.clear() </span><span class="cx"> </span><del>- c = session.query(Company).get(1) </del><ins>+ c = session.query(Company).get(id) </ins><span class="cx"> for e in c.employees: </span><span class="cx"> print e, e._instance_key </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-20 17:45:28
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1474] sqlalchemy/branches/schema/doc: edits etc</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1474</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-20 12:45:06 -0500 (Sat, 20 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>edits etc</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt">sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentdbenginetxt">sqlalchemy/branches/schema/doc/build/content/dbengine.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentpluginstxt">sqlalchemy/branches/schema/doc/build/content/plugins.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentunitofworktxt">sqlalchemy/branches/schema/doc/build/content/unitofwork.txt</a></li> <li><a href="#sqlalchemybranchesschemadocscriptsjs">sqlalchemy/branches/schema/doc/scripts.js</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocalphaapihtml">sqlalchemy/branches/schema/doc/alphaapi.html</a></li> <li><a href="#sqlalchemybranchesschemadocalphaimplementationhtml">sqlalchemy/branches/schema/doc/alphaimplementation.html</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocalphaapihtml"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/doc/alphaapi.html (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/alphaapi.html 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/alphaapi.html 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -0,0 +1,27 @@ </span><ins>+<html> +<head> + <link href="style.css" rel="stylesheet" type="text/css"></link> + <link href="docs.css" rel="stylesheet" type="text/css"></link> + <script src="scripts.js"></script> + <title>SQLAlchemy Documentation</title> +</head> +<body> + <h3>What is an Alpha API Feature?</h3> +<p><b>Alpha API</b> indicates that the best way for a particular feature to be presented hasn't been firmly settled on as of yet, and the current way is being introduced on a trial basis. Its spirit is not as much a warning that "this API might change", its more an invitation to the users saying, "heres a new idea I had. I'm not sure if this is the best way to do it. Do you like it ? Should we do this differently? Or is it good the way it is ?". Alpha API features are always small in scope and are presented in releases so that the greatest number of users get some hands-on experience with it; large-scoped API or architectural changes will always be discussed on the mailing list/Wiki first.</p> + +<p>Reasons why a feature might want to change include: + <ul> + <li>The API for the feature is too difficult to use for the typical task, and needs to be more "convenient"</li> + <li>The feature only implements a subsection of what it really should be doing</li> + <li>The feature's interface is inconsistent with that of other features which operate at a similar level</li> + <li>The feature is confusing and is often misunderstood, and would be better replaced by a more manual feature that makes the task clearer</li> + <li>The feature overlaps with another feature and effectively provides too many ways to do the same thing</li> + <li>The feature made some assumptions about the total field of use cases which is not really true, and it breaks in other scenarios</li> + </ul> + +</p> +<p>A good example of what was essentially an "alpha feature" is the <code>private=True</code> flag. This flag on a <code>relation()</code> indicates that child objects should be deleted along with the parent. After this flag experienced some usage by the SA userbase, some users remarked that a more generic and configurable way was Hibernates <code>cascade="all, delete-orphan"</code>, and also that the term <code>cascade</code> was clearer in purpose than the more ambiguous <code>private</code> keyword, which could be construed as a "private variable".</p> + +<center><input type="button" value="close window" onclick="window.close()"></center> +</body> +</html> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemadocalphaimplementationhtml"></a> <div class="addfile"><h4>Added: sqlalchemy/branches/schema/doc/alphaimplementation.html (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/alphaimplementation.html 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/alphaimplementation.html 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -0,0 +1,16 @@ </span><ins>+<html> +<head> + <link href="style.css" rel="stylesheet" type="text/css"></link> + <link href="docs.css" rel="stylesheet" type="text/css"></link> + <script src="scripts.js"></script> + <title>SQLAlchemy Documentation</title> +</head> +<body> + <h3>What is an Alpha Implementation Feature?</h3> +<p><b>Alpha Implementation</b> indicates a feature where developer confidence in its functionality has not yet been firmly established. This typically includes brand new features for which adequate unit tests have not been completed, and/or features whose scope is broad enough that its not clear what additional unit tests might be needed.</p> + +<p>Alpha implementation is not meant to discourage the usage of a feature, it is only meant to indicate that some difficulties in getting full functionality from the feature may occur, and to encourage the reporting of these difficulties either via the mailing list or through <a href="http://www.sqlalchemy.org/trac/newticket" target="_blank">submitting a ticket</a>.</p> + +<center><input type="button" value="close window" onclick="window.close()"></center> +</body> +</html> </ins><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentadv_datamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/build/content/adv_datamapping.txt 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -1,3 +1,6 @@ </span><ins>+[alpha_api]: javascript:alphaApi() +[alpha_implementation]: javascript:alphaImplementation() + </ins><span class="cx"> Advanced Data Mapping {@name=advdatamapping} </span><span class="cx"> ====================== </span><span class="cx"> </span><span class="lines">@@ -81,8 +84,50 @@ </span><span class="cx"> '_email': mytable.c.email </span><span class="cx"> }) </span><span class="cx"> </span><del>-In a later release, SQLAlchemy will also allow `_get_email` and `_set_email` to be attached directly to the "email" property created by the mapper, and will also allow this association to occur via decorators. </del><ins>+In a later release, SQLAlchemy will also allow `_get_email` and `_set_email` to be attached directly to the "email" property created by the mapper, and +will also allow this association to occur via decorators. </ins><span class="cx"> </span><ins>+ +#### Custom List Classes {@name=customlist} + +Feature Status: [Alpha API][alpha_api] + +A one-to-many or many-to-many relationship results in a list-holding element being attached to all instances of a class. Currently, this list is an instance of `sqlalchemy.util.HistoryArraySet`, is a `UserDict` instance that *decorates* an underlying list object. The implementation of this list can be controlled, and can in fact be any object that implements a `list`-style `append` and `__iter__` method. A common need is for a list-based relationship to actually be a dictionary. This can be achieved by subclassing `dict` to have `list`-like behavior. + +In this example, a class `MyClass` is defined, which is associated with a parent object `MyParent`. The collection of `MyClass` objects on each `MyParent` object will be a dictionary, storing each `MyClass` instance keyed to its `name` attribute. + + {python} + # a class to be stored in the list + class MyClass(object): + def __init__(self, name): + self.name = name + + # create a dictionary that will act like a list, and store + # instances of MyClass + class MyDict(dict): + def append(self, item): + self[item.name] = item + def __iter__(self): + return self.values() + + # parent class + class MyParent(object): + # this class-level attribute provides the class to be + # used by the 'myclasses' attribute + myclasses = MyDict + + # mappers, constructed normally + mapper(MyClass, myclass_table) + mapper(MyParent, myparent_table, properties={ + 'myclasses' : relation(MyClass) + }) + + # elements on 'myclasses' can be accessed via string keyname + myparent = MyParent() + myparent.myclasses.append(MyClass('this is myclass')) + myclass = myparent.myclasses['this is myclass'] + + </ins><span class="cx"> #### Custom Join Conditions {@name=customjoin} </span><span class="cx"> </span><span class="cx"> When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent. in fact, this "automatic" inspection can be completely circumvented using the `primaryjoin` and `secondaryjoin` arguments to `relation`, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston: </span><span class="lines">@@ -277,7 +322,6 @@ </span><span class="cx"> </span><span class="cx"> The main WHERE clause as well as the limiting clauses are coerced into a subquery; this subquery represents the desired result of objects. A containing query, which handles the eager relationships, is joined against the subquery to produce the result. </span><span class="cx"> </span><del>- </del><span class="cx"> ### More on Mapper Options {@name=options} </span><span class="cx"> </span><span class="cx"> The `options` method on the `Query` object, first introduced in [datamapping_relations_options](rel:datamapping_relations_options), produces a new `Query` object by creating a copy of the underlying `Mapper` and placing modified properties on it. The `options` method is also directly available off the `Mapper` object itself, so that the newly copied `Mapper` can be dealt with directly. The `options` method takes a variable number of `MapperOption` objects which know how to change specific things about the mapper. The five available options are `eagerload`, `lazyload`, `noload`, `deferred` and `extension`. </span><span class="lines">@@ -309,6 +353,8 @@ </span><span class="cx"> </span><span class="cx"> ### Mapping a Class with Table Inheritance {@name=inheritance} </span><span class="cx"> </span><ins>+Feature Status: [Alpha Implementation][alpha_implementation] + </ins><span class="cx"> Inheritance in databases comes in three forms: *single table inheritance*, where several types of classes are stored in one table, *concrete table inheritance*, where each type of class is stored in its own table, and *multiple table inheritance*, where the parent/child classes are stored in their own tables that are joined together in a select. </span><span class="cx"> </span><span class="cx"> There is also a concept of `polymorphic` loading, which indicates if multiple kinds of classes can be loaded in one pass. </span><span class="lines">@@ -535,7 +581,7 @@ </span><span class="cx"> </span><span class="cx"> ### Circular Mapping {@name=circular} </span><span class="cx"> </span><del>-Oftentimes it is necessary for two mappers to be related to each other. With a datamodel that consists of Users that store Addresses, you might have an Address object and want to access the "user" attribute on it, or have a User object and want to get the list of Address objects. The easiest way to do this is via the `backreference` keyword described in [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). Although even when backreferences are used, it is sometimes necessary to explicitly specify the relations on both mappers pointing to each other. </del><ins>+Oftentimes it is necessary for two mappers to be related to each other. With a datamodel that consists of Users that store Addresses, you might have an Address object and want to access the "user" attribute on it, or have a User object and want to get the list of Address objects. The easiest way to do this is via the `backref` keyword described in [datamapping_relations_backreferences](rel:datamapping_relations_backreferences). Although even when backreferences are used, it is sometimes necessary to explicitly specify the relations on both mappers pointing to each other. </ins><span class="cx"> To achieve this involves creating the first mapper by itself, then creating the second mapper referencing the first, then adding references to the first mapper to reference the second: </span><span class="cx"> </span><span class="cx"> {python} </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentdbenginetxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/dbengine.txt (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -47,7 +47,7 @@ </span><span class="cx"> </span><span class="cx"> Options that can be specified include the following: </span><span class="cx"> </span><del>-* strategy='plain' : the Strategy describes the general configuration used to create this Engine. The two available values are `plain`, which is the default, and `threadlocal`, which applies a "thread-local context" to implicit executions performed by the Engine. This context is further described in the sections below. </del><ins>+* strategy='plain' : the Strategy describes the general configuration used to create this Engine. The two available values are `plain`, which is the default, and `threadlocal`, which applies a "thread-local context" to implicit executions performed by the Engine. This context is further described in [dbengine_connections_context](rel:dbengine_connections_context). </ins><span class="cx"> * pool=None : an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections, overriding the engine's connect arguments (pooling is described in [pooling](rel:pooling)). If None, a default `Pool` (usually `QueuePool`, or `SingletonThreadPool` in the case of SQLite) will be created using the engine's connect arguments. </span><span class="cx"> </span><span class="cx"> Example: </span><span class="lines">@@ -120,7 +120,7 @@ </span><span class="cx"> </span><span class="cx"> #### Implicit Connection Contexts {@name=context} </span><span class="cx"> </span><del>-SQL operations can also be performed using the `Engine` alone without explicitly referencing a `Connection`. The operation is executed with a connection that is allocated internally. This type of connection, i.e. the automatically allocated connection when calling `execute()` directly off of an `Engine`, is called an **implicit connection**. </del><ins>+An **implicit connection** refers to connections that are allocated by the `Engine` internally. There are two general cases when this occurs: when using the various `execute()` methods that are available off the `Engine` object itself, and when calling the `execute()` method on constructed SQL objects, which are described in [sqlconstruction](rel:sqlconstruction). </ins><span class="cx"> </span><span class="cx"> {python title="Implicit Connection"} </span><span class="cx"> engine = create_engine('sqlite:///:memory:') </span><span class="lines">@@ -129,12 +129,14 @@ </span><span class="cx"> print row['col1'], row['col2'] </span><span class="cx"> result.close() </span><span class="cx"> </span><del>-When using implicit connections, the returned `ResultProxy` has a `close()` method which will return the resources used by the `Connection`. While the implicit method of execution seems redundant in light of the explicit `Connection` object available, it is essentially the same methodology used by "bound" schema and statement objects to execute themselves, so the same rules of connection allocation apply. </del><ins>+When using implicit connections, the returned `ResultProxy` has a `close()` method which will return the resources used by the underlying `Connection`. </ins><span class="cx"> </span><del>-The user has two choices, determined when the Engine is first created, as to how the resources of this connection should be used in relation to other connections. This is determined by the `strategy` argument to `create_engine()`, which has two possible values: `plain` and `threadlocal`. In `plain`, every `execute` call uses a distinct connection from the database, which is only released when the `close()` method on the Result is called, or the result object itself and its underlying Connection falls out of scope and is garbage collected. In `threadlocal`, multiple calls to `execute()` within the same thread will use the already-checked out connection resource if one is available, or if none is available will request a connection resource. </del><ins>+The `strategy` keyword argument to `create_engine()` affects the algorithm used to retreive the underlying DBAPI connection used by implicit executions. When set to `plain`, each implicit execution requests a unique connection from the connection pool, which is returned to the pool when the resulting `ResultProxy` falls out of scope (i.e. `__del__()` is called) or its `close()` method is called. If a second implicit execution occurs while the `ResultProxy` from the previous execution is still open, then a second connection is pulled from the pool. </ins><span class="cx"> </span><del>-It is crucial to note that the `plain` and `threadlocal` contexts **do not impact the connect() method on the Engine.** If you are using explicit Connection objects returned by `connect()` method, you have full control over the connection resources used. </del><ins>+When `strategy` is set to `threadlocal`, the `Engine` still checks out a connection which is closeable in the same manner via the `ResultProxy`, except the connection it checks out will be the **same** connection as one which is already checked out, assuming the operation is in the same thread. When all `ResultProxy` objects are closed, the connection is returned to the pool normally. </ins><span class="cx"> </span><ins>+It is crucial to note that the `plain` and `threadlocal` contexts **do not impact the connect() method on the Engine.** `connect()` always returns a unique connection. Implicit connections use a different method off of `Engine` for their operations called `contextual_connect()`. + </ins><span class="cx"> The `plain` strategy is better suited to an application that insures the explicit releasing of the resources used by each execution. This is because each execution uses its own distinct connection resource, and as those resources remain open, multiple connections can be checked out from the pool quickly. Since the connection pool will block further requests when too many connections have been checked out, not keeping track of this can impact an application's stability. </span><span class="cx"> </span><span class="cx"> {python title="Plain Strategy"} </span><span class="lines">@@ -155,8 +157,10 @@ </span><span class="cx"> # release connection 2 </span><span class="cx"> r2.close() </span><span class="cx"> </span><del>-The `threadlocal` strategy is better suited to a programming style which relies upon the `__del__()` method of Connection objects in order to return them to the connection pool, rather than explicitly issuing a `close()` statement upon the `Result` object. This is because all of the executions within a single thread will share the same connection, if one has already been checked out in the current thread. Using this style, an application will use only one connection per thread at most within the scope of all implicit executions. </del><ins>+Advantages to `plain` include that connection resources are immediately returned to the connection pool, without any reliance upon the `__del__()` method; there is no chance of resources being left around by a Python implementation that doesn't necessarily call `__del__()` immediately. </ins><span class="cx"> </span><ins>+The `threadlocal` strategy is better suited to a programming style which relies upon the `__del__()` method of Connection objects in order to return them to the connection pool, rather than explicitly issuing a `close()` statement upon the `ResultProxy` object. This is because all of the executions within a single thread will share the same connection, if one has already been checked out in the current thread. Using this style, an application will use only one connection per thread at most within the scope of all implicit executions. + </ins><span class="cx"> {python title="Threadlocal Strategy"} </span><span class="cx"> db = create_engine('mysql://localhost/test', strategy='threadlocal') </span><span class="cx"> </span><span class="lines">@@ -179,6 +183,8 @@ </span><span class="cx"> </span><span class="cx"> While the `close()` method is still available with the "threadlocal" strategy, it should be used carefully. Above, if we issued a `close()` call on `r1`, and then tried to further work with results from `r2`, `r2` would be in an invalid state since its connection was already returned to the pool. By relying on `__del__()` to automatically clean up resources, this condition will never occur. </span><span class="cx"> </span><ins>+Advantages to `threadlocal` include that resources can be left to clean up after themselves, application code can be more minimal, its guaranteed that only one connection is used per thread, and there is no chance of a "connection pool block", which is when an execution hangs because the current thread has already checked out all remaining resources. + </ins><span class="cx"> To get at the actual `Connection` object which is used by implicit executions, call the `contextual_connection()` method on `Engine`: </span><span class="cx"> </span><span class="cx"> {python title="Contextual Connection"} </span><span class="lines">@@ -192,8 +198,6 @@ </span><span class="cx"> True </span><span class="cx"> </span><span class="cx"> When the `plain` strategy is used, the `contextual_connection()` method is synonymous with the `connect()` method; both return a distinct connection from the pool. </span><del>- -At this point, you're probably saying, "wow, why would anyone *ever* want to use the [insert name here] strategy ??" Advantages to `plain` include that connection resources are immediately returned to the connection pool, without any reliance upon the `__del__()` method; there is no chance of resources being left around by a Python implementation that doesn't necessarily call `__del__()` immediately. Advantages to `threadlocal` include that resources can be left to clean up after themselves, application code can be more minimal, its guaranteed that only one connection is used per thread, and there is no chance of a "connection pool block", which is when an execution hangs because the current thread has already checked out all remaining resources. </del><span class="cx"> </span><span class="cx"> ### Transactions {@name=transactions} </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentpluginstxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/plugins.txt (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -1,12 +1,12 @@ </span><span class="cx"> Plugins {@name=plugins} </span><span class="cx"> ====================== </span><span class="cx"> </span><del>-(SECTION UNDER CONSTRUCTION) - </del><span class="cx"> SQLAlchemy has a variety of extensions and "mods" available which provide extra functionality to SA, either via explicit usage or by augmenting the core behavior. </span><span class="cx"> </span><span class="cx"> ### threadlocal </span><span class="cx"> </span><ins>+**Author:** Mike Bayer and Daniel Miller + </ins><span class="cx"> Establishes `threadlocal` as the default strategy for new `ComposedSQLEngine` objects, installs a threadlocal `SessionContext` that is attached to all Mappers via a global `MapperExtension`, and establishes the global `SessionContext` under the name `sqlalchemy.objectstore`. Usually this is used in combination with `Tables` that are associated with `BoundMetaData` or `DynamicMetaData`, so that the `Session` does not need to be bound to any `Engine` explicitly. </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -118,35 +118,161 @@ </span><span class="cx"> # assume "threadlocal" strategy is enabled, and there is no transaction in progress </span><span class="cx"> </span><span class="cx"> result = table.select().execute() # 'result' references a DBAPI connection, bound to the current thread </span><ins>+ </ins><span class="cx"> object = session.select() # the 'select' operation also uses the current thread's connection, </span><span class="cx"> # i.e. the same connection referenced by 'result' </span><ins>+ </ins><span class="cx"> result.close() # return the connection to the pool. now there is no connection </span><span class="cx"> # associated with the current thread. the next execution will re-check out a </span><span class="cx"> # connection and re-attach to the current thread. </span><span class="cx"> </span><span class="cx"> ### SessionContext </span><span class="cx"> </span><ins>+**Author:** Daniel Miller + </ins><span class="cx"> This plugin is a generalized version of the `objectstore` object provided by the `threadlocal` plugin: </span><span class="cx"> </span><span class="cx"> {python} </span><ins>+ import sqlalchemy + from sqlalchemy.ext.sessioncontext import SessionContext </ins><span class="cx"> </span><del>- ctx = SessionContext() </del><ins>+ ctx = SessionContext(sqlalchemy.create_session) + </ins><span class="cx"> class User(object): </span><del>- __session__ = ctx </del><ins>+ pass </ins><span class="cx"> </span><del>- mapper(User, users_table) </del><ins>+ mapper(User, users_table, extension=ctx.mapperextension) </ins><span class="cx"> </span><span class="cx"> # 'u' is automatically added to the current session of 'ctx' </span><span class="cx"> u = User() </span><span class="cx"> </span><ins>+ # get the current session and flush </ins><span class="cx"> ctx.current.flush() </span><ins>+ +The construction of each `Session` instance can be customized by providing a "creation function" which returns a new `Session`. The "scope" to which the session is associated, which by default is the current thread, can be customized by providing a "scope callable" which returns a hashable key that represents the current scope: </ins><span class="cx"> </span><ins>+ {python} + import sqlalchemy + from sqlalchemy.ext.sessioncontext import SessionContext + + # create an engine + someengine = sqlalchemy.create_engine('sqlite:///') + + # a function to return a Session bound to our engine + def make_session(): + return sqlalchemy.create_session(bind_to=someengine) + + # global declaration of "scope" + scope = "scope1" + + # a function to return the current "session scope" + def global_scope_func(): + return scope + + # create SessionContext with our two functions + ctx = SessionContext(make_session, scopefunc=global_scope_func) + + # get the session corresponding to "scope1", bound to engine "someengine": + session = ctx.current + + # switch the "scope" + scope = "scope2" + + # get the session corresponding to "scope2", bound to engine "someengine": + session = ctx.current + + </ins><span class="cx"> ### ActiveMapper </span><span class="cx"> </span><ins>+**Author:** Jonathan LaCour + +ActiveMapper is a so-called "declarative layer" which allows the construction of a class, a `Table`, and a `Mapper` all in one step: + + {python} + class Person(ActiveMapper): + class mapping: + id = column(Integer, primary_key=True) + full_name = column(String) + first_name = column(String) + middle_name = column(String) + last_name = column(String) + birth_date = column(DateTime) + ssn = column(String) + gender = column(String) + home_phone = column(String) + cell_phone = column(String) + work_phone = column(String) + prefs_id = column(Integer, foreign_key=ForeignKey('preferences.id')) + addresses = one_to_many('Address', colname='person_id', backref='person') + preferences = one_to_one('Preferences', colname='pref_id', backref='person') + + def __str__(self): + s = '%s\n' % self.full_name + s += ' * birthdate: %s\n' % (self.birth_date or 'not provided') + s += ' * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown') + s += ' * personality: %s\n' % (self.preferences.personality_type or 'Unknown') + + for address in self.addresses: + s += ' * address: %s\n' % address.address_1 + s += ' %s, %s %s\n' % (address.city, address.state, address.postal_code) + + return s + + + class Preferences(ActiveMapper): + class mapping: + __table__ = 'preferences' + id = column(Integer, primary_key=True) + favorite_color = column(String) + personality_type = column(String) + + + class Address(ActiveMapper): + class mapping: + id = column(Integer, primary_key=True) + type = column(String) + address_1 = column(String) + city = column(String) + state = column(String) + postal_code = column(String) + person_id = column(Integer, foreign_key=ForeignKey('person.id')) + +More discussion on ActiveMapper can be found at [Jonathan LaCour's Blog](http://cleverdevil.org/computing/35/declarative-mapping-with-sqlalchemy) as well as the [SQLAlchemy Wiki](http://www.sqlalchemy.org/trac/wiki/ActiveMapper). + +### SqlSoup + +**Author:** Jonathan Ellis + +SqlSoup creates mapped classes on the fly from tables. It is essentially a nicer version of the "row data gateway" pattern. + + {python} + >>> from sqlalchemy.ext.sqlsoup import SqlSoup + >>> soup = SqlSoup('sqlite://filename=:memory:') + + >>> users = soup.users.select() + >>> users.sort() + >>> users + [Class_Users(name='Bhargan Basepair',email='bas...@ex...',password='basepair',classname=None,admin=1), + Class_Users(name='Joe Student',email='st...@ex...',password='student',classname=None,admin=0)] + +Read more about SqlSoup on [Jonathan Ellis' Blog](http://spyced.blogspot.com/2006/04/introducing-sqlsoup.html). + </ins><span class="cx"> ### ProxyEngine </span><span class="cx"> </span><ins>+**Author:** Jason Pellerin + +The `ProxyEngine` is used to "wrap" an `Engine`, and via subclassing `ProxyEngine` one can instrument the functionality of an arbitrary `Engine` instance through the decorator pattern. It also provides a `connect()` method which will send all `Engine` requests to different underlying engines. Its functionality in that regard is largely superceded now by `DynamicMetaData` which is a better solution. + + {python} + from sqlalchemy.ext.proxy import ProxyEngine + proxy = ProxyEngine() + + proxy.connect('postgres://user:pw@host/db') + </ins><span class="cx"> ### SelectResults </span><span class="cx"> </span><ins>+**Author:** Jonas Borgström + </ins><span class="cx"> SelectResults gives generator-like behavior to the results returned from the `select` and `select_by` method of `Query`. It supports three modes of operation; per-query, per-mapper, and per-application. </span><span class="cx"> </span><span class="cx"> {python title="SelectResults with a Query Object"} </span><span class="lines">@@ -181,7 +307,3 @@ </span><span class="cx"> mapper(MyClass, mytable) </span><span class="cx"> session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7] </span><span class="cx"> </span><del>- -### LegacySession - -(this plugin probably doesnt even work right now) </del><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentunitofworktxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/unitofwork.txt (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/build/content/unitofwork.txt 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -1,3 +1,6 @@ </span><ins>+[alpha_api]: javascript:alphaApi() +[alpha_implementation]: javascript:alphaImplementation() + </ins><span class="cx"> Session / Unit of Work {@name=unitofwork} </span><span class="cx"> ============ </span><span class="cx"> </span><span class="lines">@@ -310,6 +313,8 @@ </span><span class="cx"> </span><span class="cx"> #### merge() {@name=merge} </span><span class="cx"> </span><ins>+Feature Status: [Alpha Implementation][alpha_implementation] + </ins><span class="cx"> `merge()` is used to return the persistent version of an instance that is not attached to this Session. When passed an instance, if an instance with its database identity already exists within this Session, it is returned. If the instance does not exist in this Session, it is loaded from the database and then returned. </span><span class="cx"> </span><span class="cx"> A future version of `merge()` will also update the Session's instance with the state of the given instance (hence the name "merge"). </span><span class="lines">@@ -328,6 +333,8 @@ </span><span class="cx"> </span><span class="cx"> ### Cascade rules {@name=cascade} </span><span class="cx"> </span><ins>+Feature Status: [Alpha Implementation][alpha_implementation] + </ins><span class="cx"> Mappers support the concept of configurable *cascade* behavior on `relation()`s. This behavior controls how the Session should treat the instances that have a parent-child relationship with another instance that is operated upon by the Session. Cascade is indicated as a comma-separated list of string keywords, with the possible values `all`, `delete`, `save-update`, `refresh-expire`, `merge`, `expunge`, and `delete-orphan`. </span><span class="cx"> </span><span class="cx"> Cascading is configured by setting the `cascade` keyword argument on a `relation()`: </span></span></pre></div> <a id="sqlalchemybranchesschemadocscriptsjs"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/scripts.js (1473 => 1474)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/scripts.js 2006-05-19 00:13:01 UTC (rev 1473) +++ sqlalchemy/branches/schema/doc/scripts.js 2006-05-20 17:45:06 UTC (rev 1474) </span><span class="lines">@@ -16,3 +16,10 @@ </span><span class="cx"> } </span><span class="cx"> } </span><span class="cx"> </span><ins>+function alphaApi() { + window.open("alphaapi.html", "_blank", "width=600,height=400, scrollbars=yes,resizable=yes,toolbar=no"); +} + +function alphaImplementation() { + window.open("alphaimplementation.html", "_blank", "width=600,height=400, scrollbars=yes,resizable=yes,toolbar=no"); +} </ins><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-19 00:13:25
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1473] sqlalchemy/branches/schema/test: overhaul to Mapper's "select_table" attribute, now spawns a second mapper to handle all</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1473</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-18 19:13:01 -0500 (Thu, 18 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>overhaul to Mapper's "select_table" attribute, now spawns a second mapper to handle all select operations, and also is the target for properties. "select_table" no longer impacts primary mapper functions, simplifies column/property targeting issues. also some fixes to better allow inherited custom properties to propigate. new unittest for polymorph example added, runs through eight sets of options to test various things that were broken before this checkin some tweaks to schema using the name of tables 'Connectable' now has more methods that are shared between Conneciton and ComposedSQLEngine FromClause has additional "keys_ok" option on corresponding_column() which produces a more liberal lookup, used by mapper.translate_row edits to dbengine docs</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdbenginetxt">sqlalchemy/branches/schema/doc/build/content/dbengine.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyschemapy">sqlalchemy/branches/schema/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> <li><a href="#sqlalchemybranchesschematestpolymorphpy">sqlalchemy/branches/schema/test/polymorph.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentdbenginetxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/dbengine.txt (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/doc/build/content/dbengine.txt 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -76,11 +76,11 @@ </span><span class="cx"> </span><span class="cx"> ### Using Connections {@name=connections} </span><span class="cx"> </span><del>-In this section we describe the explicit interface available on Engine. Note that when using the Object Relational Mapper (ORM) as well as when dealing with only with "bound" metadata objects (described later), SQLAlchemy deals with the Engine for you and you generally don't need to know much about it; in those cases, you can skip this section and go to [metadata](rel:metadata). </del><ins>+In this section we describe the SQL execution interface available from an `Engine` instance. Note that when using the Object Relational Mapper (ORM) as well as when dealing with with "bound" metadata objects (described later), SQLAlchemy deals with the Engine for you and you generally don't need to know much about it; in those cases, you can skip this section and go to [metadata](rel:metadata). </ins><span class="cx"> </span><del>-The Engine provides methods by which literal SQL text as well as SQL clause constructs can be compiled and executed. </del><ins>+The Engine provides a `connect()` method which returns a `Connection` object. This object provides methods by which literal SQL text as well as SQL clause constructs can be compiled and executed. </ins><span class="cx"> </span><del>- {python title="Explicit Connection"} </del><ins>+ {python} </ins><span class="cx"> engine = create_engine('sqlite:///:memory:') </span><span class="cx"> connection = engine.connect() </span><span class="cx"> result = connection.execute("select * from mytable where col1=:col1", col1=5) </span><span class="lines">@@ -90,15 +90,7 @@ </span><span class="cx"> </span><span class="cx"> The `close` method on `Connection` does not actually remove the underlying connection to the database, but rather indicates that the underlying resources can be returned to the connection pool. When using the `connect()` method, the DBAPI connection referenced by the `Connection` object is not referenced anywhere else. </span><span class="cx"> </span><del>- {python title="Implicit Connection"} - engine = create_engine('sqlite:///:memory:') - result = engine.execute("select * from mytable where col1=:col1", col1=5) - for row in result: - print row['col1'], row['col2'] - result.close() </del><span class="cx"> </span><del>-When executing off the Engine directly, a Connection is created and used automatically. The returned `ResultProxy` then has a `close()` method, which will return the resources used by the `Connection`. This is a more abbreviated style of usage which is also the method used when dealing with "bound" schema and statement objects, which are described later. - </del><span class="cx"> In both execution styles above, the `Connection` object will also automatically return its resources to the connection pool when the object is garbage collected, i.e. its `__del__()` method is called. When using the standard C implementation of Python, this method is usually called immediately as soon as the object is dereferenced. With other Python implementations such as Jython, this is not so guaranteed. </span><span class="cx"> </span><span class="cx"> The execute method on `Engine` and `Connection` can also receive SQL clause constructs as well, which are described in [sql](rel:sql): </span><span class="lines">@@ -110,7 +102,7 @@ </span><span class="cx"> print row['col1'], row['col2'] </span><span class="cx"> connection.close() </span><span class="cx"> </span><del>-In many cases, the `Connection` and `Engine` can be used interchangeably; as they both provide an `engine` attribute as well as similar `execute` methods, most SQLAlchemy functions which take an `Engine` as a parameter with which to execute SQL will also accept a `Connection`: </del><ins>+Both `Connection` and `Engine` fulfill an interface known as `Connectable` which specifies common functionality between the two objects, such as getting a `Connection` and executing queries. Therefore, most SQLAlchemy functions which take an `Engine` as a parameter with which to execute SQL will also accept a `Connection`: </ins><span class="cx"> </span><span class="cx"> {python title="Specify Engine or Connection"} </span><span class="cx"> engine = create_engine('sqlite:///:memory:') </span><span class="lines">@@ -128,8 +120,19 @@ </span><span class="cx"> </span><span class="cx"> #### Implicit Connection Contexts {@name=context} </span><span class="cx"> </span><del>-"Implicit" connections refer to the example above when the `execute()` method is called directly off the `Engine` object, *without* the usage of a `Connection` object, and resources are released by calling the `close()` method on the result object. When using "implicit" connections, the user has two choices, determined when the Engine is first created, as to how the resources of this connection should be used in relation to other connections. This is determined by the `strategy` argument to `create_engine()`, which has two possible values: `plain` and `threadlocal`. In `plain`, every `execute` call uses a distinct connection from the database, which is only released when the `close()` method on the Result is called, or the result object itself and its underlying Connection falls out of scope and is garbage collected. In `threadlocal`, multiple calls to `execute()` within the same thread will use the already-checked out connection resource! if one is available, or if none is available will request a connection resource. </del><ins>+SQL operations can also be performed using the `Engine` alone without explicitly referencing a `Connection`. The operation is executed with a connection that is allocated internally. This type of connection, i.e. the automatically allocated connection when calling `execute()` directly off of an `Engine`, is called an **implicit connection**. </ins><span class="cx"> </span><ins>+ {python title="Implicit Connection"} + engine = create_engine('sqlite:///:memory:') + result = engine.execute("select * from mytable where col1=:col1", col1=5) + for row in result: + print row['col1'], row['col2'] + result.close() + +When using implicit connections, the returned `ResultProxy` has a `close()` method which will return the resources used by the `Connection`. While the implicit method of execution seems redundant in light of the explicit `Connection` object available, it is essentially the same methodology used by "bound" schema and statement objects to execute themselves, so the same rules of connection allocation apply. + +The user has two choices, determined when the Engine is first created, as to how the resources of this connection should be used in relation to other connections. This is determined by the `strategy` argument to `create_engine()`, which has two possible values: `plain` and `threadlocal`. In `plain`, every `execute` call uses a distinct connection from the database, which is only released when the `close()` method on the Result is called, or the result object itself and its underlying Connection falls out of scope and is garbage collected. In `threadlocal`, multiple calls to `execute()` within the same thread will use the already-checked out connection resource if one is available, or if none is available will request a connection resource. + </ins><span class="cx"> It is crucial to note that the `plain` and `threadlocal` contexts **do not impact the connect() method on the Engine.** If you are using explicit Connection objects returned by `connect()` method, you have full control over the connection resources used. </span><span class="cx"> </span><span class="cx"> The `plain` strategy is better suited to an application that insures the explicit releasing of the resources used by each execution. This is because each execution uses its own distinct connection resource, and as those resources remain open, multiple connections can be checked out from the pool quickly. Since the connection pool will block further requests when too many connections have been checked out, not keeping track of this can impact an application's stability. </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -157,6 +157,13 @@ </span><span class="cx"> def contextual_connect(self): </span><span class="cx"> """returns a Connection object which may be part of an ongoing context.""" </span><span class="cx"> raise NotImplementedError() </span><ins>+ def create(self, entity, **kwargs): + """creates a table or index given an appropriate schema object.""" + raise NotImplementedError() + def drop(self, entity, **kwargs): + raise NotImplementedError() + def execute(self, object, *multiparams, **params): + raise NotImplementedError() </ins><span class="cx"> def _not_impl(self): </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> engine = property(_not_impl, doc="returns the Engine which this Connectable is associated with.") </span><span class="lines">@@ -532,6 +539,7 @@ </span><span class="cx"> try: </span><span class="cx"> rec = self.props[key._label.lower()] </span><span class="cx"> except KeyError: </span><ins>+ print "DIDNT HAVE", key._label.lower() </ins><span class="cx"> try: </span><span class="cx"> rec = self.props[key.key.lower()] </span><span class="cx"> except KeyError: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -79,6 +79,8 @@ </span><span class="cx"> self.polymorphic_map = {} </span><span class="cx"> else: </span><span class="cx"> self.polymorphic_map = polymorphic_map </span><ins>+ self.__surrogate_mapper = None + self._surrogate_parent = None </ins><span class="cx"> </span><span class="cx"> if not issubclass(class_, object): </span><span class="cx"> raise exceptions.ArgumentError("Class '%s' is not a new-style class" % class_.__name__) </span><span class="lines">@@ -152,10 +154,9 @@ </span><span class="cx"> </span><span class="cx"> if select_table is not None: </span><span class="cx"> self.select_table = select_table </span><del>- self.unjoined_table = self.select_table </del><span class="cx"> else: </span><span class="cx"> self.select_table = self.mapped_table </span><del>- self.unjoined_table = self.local_table </del><ins>+ self.unjoined_table = self.local_table </ins><span class="cx"> </span><span class="cx"> # locate all tables contained within the "table" passed in, which </span><span class="cx"> # may be a join or other construct </span><span class="lines">@@ -169,9 +170,9 @@ </span><span class="cx"> if k.table != self.mapped_table: </span><span class="cx"> # associate pk cols from subtables to the "main" table </span><span class="cx"> self.pks_by_table.setdefault(self.mapped_table, util.HashSet(ordered=True)).append(k) </span><del>- # TODO: need select_table, local_table properly accounted for when custom primary key is sent </del><ins>+ # TODO: need local_table properly accounted for when custom primary key is sent </ins><span class="cx"> else: </span><del>- for t in self.tables + [self.mapped_table, self.select_table]: </del><ins>+ for t in self.tables + [self.mapped_table]: </ins><span class="cx"> try: </span><span class="cx"> l = self.pks_by_table[t] </span><span class="cx"> except KeyError: </span><span class="lines">@@ -191,22 +192,31 @@ </span><span class="cx"> # table columns mapped to lists of MapperProperty objects </span><span class="cx"> # using a list allows a single column to be defined as </span><span class="cx"> # populating multiple object attributes </span><del>- self.columntoproperty = TranslatingDict(self.select_table) </del><ins>+ self.columntoproperty = TranslatingDict(self.mapped_table) </ins><span class="cx"> </span><span class="cx"> # load custom properties </span><span class="cx"> if properties is not None: </span><span class="cx"> for key, prop in properties.iteritems(): </span><span class="cx"> self.add_property(key, prop, False) </span><span class="cx"> </span><ins>+ if inherits is not None: + inherits._inheriting_mappers.add(self) + for key, prop in inherits.props.iteritems(): + if not self.props.has_key(key): + p = prop.copy() + if p.adapt(self): + self.add_property(key, p, init=False) + </ins><span class="cx"> # load properties from the main table object, </span><span class="cx"> # not overriding those set up in the 'properties' argument </span><del>- for column in self.select_table.columns: </del><ins>+ for column in self.mapped_table.columns: + + if self.columntoproperty.has_key(column): + continue + </ins><span class="cx"> if not self.columns.has_key(column.key): </span><span class="cx"> self.columns[column.key] = column </span><span class="cx"> </span><del>- if self.columntoproperty.has_key(column): - continue - </del><span class="cx"> prop = self.props.get(column.key, None) </span><span class="cx"> if prop is None: </span><span class="cx"> prop = ColumnProperty(column) </span><span class="lines">@@ -229,7 +239,7 @@ </span><span class="cx"> # back to the property </span><span class="cx"> proplist = self.columntoproperty.setdefault(column, []) </span><span class="cx"> proplist.append(prop) </span><del>- </del><ins>+ </ins><span class="cx"> if not non_primary and (not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper())): </span><span class="cx"> sessionlib.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><span class="lines">@@ -240,54 +250,71 @@ </span><span class="cx"> if isinstance(self.polymorphic_map[key], type): </span><span class="cx"> self.polymorphic_map[key] = class_mapper(self.polymorphic_map[key]) </span><span class="cx"> </span><del>- if inherits is not None: - inherits._inheriting_mappers.add(self) - for key, prop in inherits.props.iteritems(): - if not self.props.has_key(key): - self.props[key] = prop.copy() - self.props[key].parent = self - # self.props[key].key = None # force re-init </del><span class="cx"> l = [(key, prop) for key, prop in self.props.iteritems()] </span><span class="cx"> for key, prop in l: </span><span class="cx"> if getattr(prop, 'key', None) is None: </span><span class="cx"> prop.init(key, self) </span><span class="cx"> </span><ins>+ # select_table specified...set up a surrogate mapper that will be used for selects + # select_table has to encompass all the columns of the mapped_table either directly + # or through proxying relationships + if self.select_table is not self.mapped_table: + props = {} + if properties is not None: + for key, prop in properties.iteritems(): + if sql.is_column(prop): + props[key] = self.select_table.corresponding_column(prop) + elif (isinstance(column, list) and sql.is_column(column[0])): + props[key] = [self.select_table.corresponding_column(c) for c in prop] + self.__surrogate_mapper = Mapper(self.class_, self.select_table, non_primary=True, properties=props, polymorphic_map=self.polymorphic_map, polymorphic_on=self.polymorphic_on) + </ins><span class="cx"> def add_polymorphic_mapping(self, key, class_or_mapper, entity_name=None): </span><span class="cx"> if isinstance(class_or_mapper, type): </span><span class="cx"> class_or_mapper = class_mapper(class_or_mapper, entity_name=entity_name) </span><span class="cx"> self.polymorphic_map[key] = class_or_mapper </span><del>- - </del><span class="cx"> </span><del>- </del><span class="cx"> def add_properties(self, dict_of_properties): </span><span class="cx"> """adds the given dictionary of properties to this mapper, using add_property.""" </span><span class="cx"> for key, value in dict_of_properties.iteritems(): </span><span class="cx"> self.add_property(key, value, True) </span><ins>+ + def _create_prop_from_column(self, column, skipmissing=False): + if sql.is_column(column): + try: + column = self.mapped_table.corresponding_column(column) + except KeyError: + if skipmissing: + return + raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) + return ColumnProperty(column) + elif isinstance(column, list) and sql.is_column(column[0]): + try: + column = [self.mapped_table.corresponding_column(c) for c in column] + except KeyError, e: + # TODO: want to take the columns we have from this + if skipmissing: + return + raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) + return ColumnProperty(*column) + else: + return None </ins><span class="cx"> </span><del>- def add_property(self, key, prop, init=True): </del><ins>+ def add_property(self, key, prop, init=True, skipmissing=False): </ins><span class="cx"> """adds an additional property to this mapper. this is the same as if it were </span><span class="cx"> specified within the 'properties' argument to the constructor. if the named </span><span class="cx"> property already exists, this will replace it. Useful for </span><span class="cx"> circular relationships, or overriding the parameters of auto-generated properties </span><span class="cx"> such as backreferences.""" </span><span class="cx"> </span><del>- if sql.is_column(prop): - try: - prop = self.select_table.corresponding_column(prop) - except KeyError: - raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % prop._label) - self.columns[key] = prop - prop = ColumnProperty(prop) - elif isinstance(prop, list) and sql.is_column(prop[0]): - try: - prop = [self.select_table.corresponding_column(p) for p in prop] - except KeyError, e: - raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0]) - self.columns[key] = prop[0] - prop = ColumnProperty(*prop) </del><ins>+ if not isinstance(prop, MapperProperty): + prop = self._create_prop_from_column(prop, skipmissing=skipmissing) + if prop is None: + raise exceptions.ArgumentError("'%s' is not an instance of MapperProperty or Column" % repr(prop)) + </ins><span class="cx"> self.props[key] = prop </span><ins>+ </ins><span class="cx"> if isinstance(prop, ColumnProperty): </span><ins>+ self.columns[key] = prop.columns[0] </ins><span class="cx"> for col in prop.columns: </span><span class="cx"> proplist = self.columntoproperty.setdefault(col, []) </span><span class="cx"> proplist.append(prop) </span><span class="lines">@@ -297,11 +324,11 @@ </span><span class="cx"> </span><span class="cx"> for mapper in self._inheriting_mappers: </span><span class="cx"> p = prop.copy() </span><del>- p.parent = mapper - mapper.add_property(key, p, init=False) </del><ins>+ if p.adapt(mapper): + mapper.add_property(key, p, init=False) </ins><span class="cx"> </span><span class="cx"> def __str__(self): </span><del>- return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.select_table.name </del><ins>+ return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + self.mapped_table.name </ins><span class="cx"> </span><span class="cx"> def _is_primary_mapper(self): </span><span class="cx"> """returns True if this mapper is the primary mapper for its class key (class + entity_name)""" </span><span class="lines">@@ -416,7 +443,7 @@ </span><span class="cx"> </span><span class="cx"> def identity(self, instance): </span><span class="cx"> """returns the identity (list of primary key values) for the given instance. The list of values can be fed directly into the get() method as mapper.get(*key).""" </span><del>- return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.select_table]] </del><ins>+ return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.mapped_table]] </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> def copy(self, **kwargs): </span><span class="lines">@@ -690,21 +717,23 @@ </span><span class="cx"> yield c </span><span class="cx"> </span><span class="cx"> def _row_identity_key(self, row): </span><del>- return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.select_table], self.entity_name) </del><ins>+ return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.mapped_table], self.entity_name) </ins><span class="cx"> </span><ins>+ def get_select_mapper(self): + return self.__surrogate_mapper or self + </ins><span class="cx"> def _instance(self, session, row, imap, result = None, populate_existing = False): </span><span class="cx"> """pulls an object instance from the given row and appends it to the given result </span><span class="cx"> list. if the instance already exists in the given identity map, its not added. in </span><span class="cx"> either case, executes all the property loaders on the instance to also process extra </span><span class="cx"> information in the row.""" </span><del>- </del><ins>+ </ins><span class="cx"> if self.polymorphic_on is not None: </span><span class="cx"> discriminator = row[self.polymorphic_on] </span><span class="cx"> mapper = self.polymorphic_map[discriminator] </span><span class="cx"> if mapper is not self: </span><span class="cx"> row = self.translate_row(mapper, row) </span><span class="cx"> return mapper._instance(session, row, imap, result=result, populate_existing=populate_existing) </span><del>- </del><span class="cx"> </span><span class="cx"> # look in main identity map. if its there, we dont do anything to it, </span><span class="cx"> # including modifying any of its related items lists, as its already </span><span class="lines">@@ -730,7 +759,7 @@ </span><span class="cx"> if not exists: </span><span class="cx"> # check if primary key cols in the result are None - this indicates </span><span class="cx"> # an instance of the object is not present in the row </span><del>- for col in self.pks_by_table[self.select_table]: </del><ins>+ for col in self.pks_by_table[self.mapped_table]: </ins><span class="cx"> if row[col] is None: </span><span class="cx"> return None </span><span class="cx"> # plugin point </span><span class="lines">@@ -768,8 +797,8 @@ </span><span class="cx"> """attempts to take a row and translate its values to a row that can </span><span class="cx"> be understood by another mapper.""" </span><span class="cx"> newrow = util.DictDecorator(row) </span><del>- for c in tomapper.select_table.c: - c2 = self.select_table.corresponding_column(c) </del><ins>+ for c in tomapper.mapped_table.c: + c2 = self.mapped_table.corresponding_column(c, keys_ok=True, raiseerr=True) </ins><span class="cx"> newrow[c] = row[c2] </span><span class="cx"> return newrow </span><span class="cx"> </span><span class="lines">@@ -880,6 +909,14 @@ </span><span class="cx"> self.key = key </span><span class="cx"> self.parent = parent </span><span class="cx"> self.do_init(key, parent) </span><ins>+ def adapt(self, newparent): + """adapts this MapperProperty to a new parent, assuming the new parent is an inheriting + descendant of the old parent. Should return True if the adaptation was successful, or + False if this MapperProperty cannot be adapted to the new parent (the case for this is, + the parent mapper has a polymorphic select, and this property represents a column that is not + represented in the new mapper's mapped table)""" + self.parent = newparent + return True </ins><span class="cx"> def do_init(self, key, parent): </span><span class="cx"> """template method for subclasses""" </span><span class="cx"> pass </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -186,11 +186,13 @@ </span><span class="cx"> else: </span><span class="cx"> self.mapper = self.argument </span><span class="cx"> </span><ins>+ self.mapper = self.mapper.get_select_mapper() + </ins><span class="cx"> if self.association is not None: </span><span class="cx"> if isinstance(self.association, type): </span><span class="cx"> self.association = mapper.class_mapper(self.association) </span><span class="cx"> </span><del>- self.target = self.mapper.select_table </del><ins>+ self.target = self.mapper.mapped_table </ins><span class="cx"> self.key = key </span><span class="cx"> self.parent = parent </span><span class="cx"> </span><span class="lines">@@ -316,8 +318,8 @@ </span><span class="cx"> </span><span class="cx"> The list of rules is used within commits by the _synchronize() method when dependent </span><span class="cx"> objects are processed.""" </span><del>- parent_tables = util.HashSet(self.parent.tables + [self.parent.mapped_table, self.parent.select_table]) - target_tables = util.HashSet(self.mapper.tables + [self.mapper.mapped_table, self.mapper.select_table]) </del><ins>+ parent_tables = util.HashSet(self.parent.tables + [self.parent.mapped_table]) + target_tables = util.HashSet(self.mapper.tables + [self.mapper.mapped_table]) </ins><span class="cx"> </span><span class="cx"> self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction) </span><span class="cx"> if self.direction == sync.MANYTOMANY: </span><span class="lines">@@ -360,7 +362,7 @@ </span><span class="cx"> # to possibly save a DB round trip </span><span class="cx"> if self.use_get: </span><span class="cx"> ident = [] </span><del>- for primary_key in self.mapper.pks_by_table[self.mapper.select_table]: </del><ins>+ for primary_key in self.mapper.pks_by_table[self.mapper.mapped_table]: </ins><span class="cx"> bind = self.lazyreverse[primary_key] </span><span class="cx"> ident.append(params[bind.key]) </span><span class="cx"> return self.mapper.using(session).get(ident) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -15,6 +15,8 @@ </span><span class="cx"> self.mapper = class_mapper(class_or_mapper, entity_name=entity_name) </span><span class="cx"> else: </span><span class="cx"> self.mapper = class_or_mapper </span><ins>+ self.mapper = self.mapper.get_select_mapper() + </ins><span class="cx"> self.always_refresh = kwargs.pop('always_refresh', self.mapper.always_refresh) </span><span class="cx"> self.order_by = kwargs.pop('order_by', self.mapper.order_by) </span><span class="cx"> self.extension = kwargs.pop('extension', self.mapper.extension) </span><span class="lines">@@ -31,7 +33,6 @@ </span><span class="cx"> else: </span><span class="cx"> return self._session </span><span class="cx"> table = property(lambda s:s.mapper.select_table) </span><del>- props = property(lambda s:s.mapper.props) </del><span class="cx"> session = property(_get_session) </span><span class="cx"> </span><span class="cx"> def get(self, ident, **kwargs): </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -164,12 +164,12 @@ </span><span class="cx"> return self.bind_to </span><span class="cx"> elif self.binds.has_key(mapper): </span><span class="cx"> return self.binds[mapper] </span><del>- elif self.binds.has_key(mapper.select_table): - return self.binds[mapper.select_table] </del><ins>+ elif self.binds.has_key(mapper.mapped_table): + return self.binds[mapper.mapped_table] </ins><span class="cx"> elif self.bind_to is not None: </span><span class="cx"> return self.bind_to </span><span class="cx"> else: </span><del>- e = mapper.select_table.engine </del><ins>+ e = mapper.mapped_table.engine </ins><span class="cx"> if e is None: </span><span class="cx"> raise exceptions.InvalidRequestError("Could not locate any Engine bound to mapper '%s'" % str(mapper)) </span><span class="cx"> return e </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/schema.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/schema.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -214,15 +214,15 @@ </span><span class="cx"> issue a SQL DROP statement.""" </span><span class="cx"> key = _get_table_key(self.name, self.schema) </span><span class="cx"> del self.metadata.tables[key] </span><del>- def create(self, engine=None): - if engine is not None: - engine.create(self) </del><ins>+ def create(self, connectable=None): + if connectable is not None: + connectable.create(self) </ins><span class="cx"> else: </span><span class="cx"> self.engine.create(self) </span><span class="cx"> return self </span><del>- def drop(self, engine=None): - if engine is not None: - engine.drop(self) </del><ins>+ def drop(self, connectable=None): + if connectable is not None: + connectable.drop(self) </ins><span class="cx"> else: </span><span class="cx"> self.engine.drop(self) </span><span class="cx"> def tometadata(self, metadata, schema=None): </span><span class="lines">@@ -309,7 +309,11 @@ </span><span class="cx"> </span><span class="cx"> def __str__(self): </span><span class="cx"> if self.table is not None: </span><del>- return str(self.table) + "." + self.name </del><ins>+ tname = self.table.displayname + if tname is not None: + return tname + "." + self.name + else: + return self.name </ins><span class="cx"> else: </span><span class="cx"> return self.name </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -656,6 +656,12 @@ </span><span class="cx"> """represents an element that can be used within the FROM clause of a SELECT statement.""" </span><span class="cx"> def __init__(self, from_name = None): </span><span class="cx"> self.from_name = self.name = from_name </span><ins>+ def _display_name(self): + if self.named_with_column(): + return self.name + else: + return None + displayname = property(_display_name) </ins><span class="cx"> def _get_from_objects(self): </span><span class="cx"> # this could also be [self], at the moment it doesnt matter to the Select object </span><span class="cx"> return [] </span><span class="lines">@@ -681,7 +687,7 @@ </span><span class="cx"> if not hasattr(self, '_oid_column'): </span><span class="cx"> self._oid_column = self._locate_oid_column() </span><span class="cx"> return self._oid_column </span><del>- def corresponding_column(self, column, raiseerr=True): </del><ins>+ def corresponding_column(self, column, raiseerr=True, keys_ok=False): </ins><span class="cx"> """given a ColumnElement, return the ColumnElement object from this </span><span class="cx"> Selectable which corresponds to that original Column via a proxy relationship.""" </span><span class="cx"> for c in column.orig_set: </span><span class="lines">@@ -690,10 +696,15 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> else: </span><ins>+ if keys_ok: + try: + return self.c[column.key] + except KeyError: + pass </ins><span class="cx"> if not raiseerr: </span><span class="cx"> return None </span><span class="cx"> else: </span><del>- raise exceptions.InvalidRequestError("cant get orig for " + str(column) + " with table " + str(column.table.name) + " from table " + str(self.name)) </del><ins>+ raise exceptions.InvalidRequestError("Given column '%s', attached to table '%s', failed to locate a corresponding column from table '%s'" % (str(column), str(column.table), self.name)) </ins><span class="cx"> </span><span class="cx"> def _get_exported_attribute(self, name): </span><span class="cx"> try: </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/test/alltests.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -52,6 +52,7 @@ </span><span class="cx"> 'manytomany', </span><span class="cx"> 'onetoone', </span><span class="cx"> 'inheritance', </span><ins>+ 'polymorph', </ins><span class="cx"> </span><span class="cx"> # extensions </span><span class="cx"> 'proxy_engine', </span></span></pre></div> <a id="sqlalchemybranchesschematestpolymorphpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/polymorph.py (1472 => 1473)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/polymorph.py 2006-05-18 14:16:56 UTC (rev 1472) +++ sqlalchemy/branches/schema/test/polymorph.py 2006-05-19 00:13:01 UTC (rev 1473) </span><span class="lines">@@ -1,5 +1,169 @@ </span><del>-# test basic polymorphic relationship with a single tasble </del><ins>+import testbase +from sqlalchemy import * +import sets </ins><span class="cx"> </span><del>-# test polymorphic relationship with multiple tables </del><ins>+# test classes +class Person(object): + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + setattr(self, key, value) + def get_name(self): + try: + return getattr(self, 'person_name') + except AttributeError: + return getattr(self, 'name') + def __repr__(self): + return "Ordinary person %s" % self.get_name() +class Engineer(Person): + def __repr__(self): + return "Engineer %s, status %s, engineer_name %s, primary_language %s" % (self.get_name(), self.status, self.engineer_name, self.primary_language) +class Manager(Person): + def __repr__(self): + return "Manager %s, status %s, manager_name %s" % (self.get_name(), self.status, self.manager_name) +class Company(object): + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): + setattr(self, key, value) + def __repr__(self): + return "Company %s" % self.name </ins><span class="cx"> </span><del>-# test that loading an object A from its non-polymorphic mapper returns the same identity as from its polymorphic mapper </del><span class="cx">\ No newline at end of file </span><ins>+class MultipleTableTest(testbase.PersistTest): + def setUpAll(self, use_person_column=False): + global companies, people, engineers, managers, metadata + metadata = BoundMetaData(testbase.db) + + # a table to store companies + companies = Table('companies', metadata, + Column('company_id', Integer, primary_key=True), + Column('name', String(50))) + + # we will define an inheritance relationship between the table "people" and "engineers", + # and a second inheritance relationship between the table "people" and "managers" + people = Table('people', metadata, + Column('person_id', Integer, primary_key=True), + Column('company_id', Integer, ForeignKey('companies.company_id')), + Column('name', String(50)), + Column('type', String(30))) + + engineers = Table('engineers', metadata, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('engineer_name', String(50)), + Column('primary_language', String(50)), + ) + + managers = Table('managers', metadata, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('manager_name', String(50)) + ) + + metadata.create_all() + + def tearDownAll(self): + metadata.drop_all() + + def tearDown(self): + clear_mappers() + for t in metadata.table_iterator(reverse=True): + t.delete().execute() + + def test_f_f_f(self): + self.do_test(False, False, False) + def test_f_f_t(self): + self.do_test(False, False, True) + def test_f_t_f(self): + self.do_test(False, True, False) + def test_f_t_t(self): + self.do_test(False, True, True) + def test_t_f_f(self): + self.do_test(True, False, False) + def test_t_f_t(self): + self.do_test(True, False, True) + def test_t_t_f(self): + self.do_test(True, True, False) + def test_t_t_t(self): + self.do_test(True, True, True) + + + def do_test(self, include_base=False, lazy_relation=True, redefine_colprop=False): + """tests the polymorph.py example, with several options: + + include_base - whether or not to include the base 'person' type in the union. + lazy_relation - whether or not the Company relation to People is lazy or eager. + redefine_colprop - if we redefine the 'name' column to be 'people_name' on the base Person class + """ + # create a union that represents both types of joins. + if include_base: + person_join = polymorphic_union( + { + 'engineer':people.join(engineers), + 'manager':people.join(managers), + 'person':people.select(people.c.type=='person'), + }, None, 'pjoin') + else: + person_join = polymorphic_union( + { + 'engineer':people.join(engineers), + 'manager':people.join(managers), + }, None, 'pjoin') + + if redefine_colprop: + person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_identity='person', properties= {'person_name':people.c.name}) + else: + person_mapper = mapper(Person, people, select_table=person_join, polymorphic_on=person_join.c.type, polymorphic_identity='person') + + mapper(Engineer, engineers, inherits=person_mapper, polymorphic_identity='engineer') + mapper(Manager, managers, inherits=person_mapper, polymorphic_identity='manager') + + mapper(Company, companies, properties={ + 'employees': relation(Person, lazy=lazy_relation, private=True, backref='company') + }) + + if redefine_colprop: + person_attribute_name = 'person_name' + else: + person_attribute_name = 'name' + + session = create_session() + c = Company(name='company1') + c.employees.append(Manager(status='AAB', manager_name='manager1', **{person_attribute_name:'pointy haired boss'})) + c.employees.append(Engineer(status='BBA', engineer_name='engineer1', primary_language='java', **{person_attribute_name:'dilbert'})) + if include_base: + c.employees.append(Person(status='HHH', **{person_attribute_name:'joesmith'})) + c.employees.append(Engineer(status='CGG', engineer_name='engineer2', primary_language='python', **{person_attribute_name:'wally'})) + c.employees.append(Manager(status='ABA', manager_name='manager2', **{person_attribute_name:'jsmith'})) + session.save(c) + print session.new + session.flush() + session.clear() + + c = session.query(Company).get(1) + for e in c.employees: + print e, e._instance_key, e.company + if include_base: + assert sets.Set([e.get_name() for e in c.employees]) == sets.Set(['pointy haired boss', 'dilbert', 'joesmith', 'wally', 'jsmith']) + else: + assert sets.Set([e.get_name() for e in c.employees]) == sets.Set(['pointy haired boss', 'dilbert', 'wally', 'jsmith']) + print "\n" + + + dilbert = session.query(Person).selectfirst(person_join.c.name=='dilbert') + dilbert2 = session.query(Engineer).selectfirst(people.c.name=='dilbert') + assert dilbert is dilbert2 + + dilbert.engineer_name = 'hes dibert!' + + session.flush() + session.clear() + + c = session.query(Company).get(1) + for e in c.employees: + print e, e._instance_key + + session.delete(c) + session.flush() + +if __name__ == "__main__": + testbase.main() + </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-18 14:17:09
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1472] sqlalchemy/trunk/test: c.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1472</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-18 09:16:56 -0500 (Thu, 18 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>c. martinez' fix to slicing logic</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymodsselectresultspy">sqlalchemy/trunk/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemytrunktestselectresultspy">sqlalchemy/trunk/test/selectresults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymodsselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mods/selectresults.py (1471 => 1472)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mods/selectresults.py 2006-05-18 14:15:34 UTC (rev 1471) +++ sqlalchemy/trunk/lib/sqlalchemy/mods/selectresults.py 2006-05-18 14:16:56 UTC (rev 1472) </span><span class="lines">@@ -70,11 +70,11 @@ </span><span class="cx"> else: </span><span class="cx"> res = self.clone() </span><span class="cx"> if start is not None and stop is not None: </span><del>- res._ops.update(dict(offset=start, limit=stop-start)) </del><ins>+ res._ops.update(dict(offset=self._ops.get('offset', 0)+start, limit=stop-start)) </ins><span class="cx"> elif start is None and stop is not None: </span><span class="cx"> res._ops.update(dict(limit=stop)) </span><span class="cx"> elif start is not None and stop is None: </span><del>- res._ops.update(dict(offset=start)) </del><ins>+ res._ops.update(dict(offset=self._ops.get('offset', 0)+start)) </ins><span class="cx"> if item.step is not None: </span><span class="cx"> return list(res)[None:None:item.step] </span><span class="cx"> else: </span></span></pre></div> <a id="sqlalchemytrunktestselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/selectresults.py (1471 => 1472)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/selectresults.py 2006-05-18 14:15:34 UTC (rev 1471) +++ sqlalchemy/trunk/test/selectresults.py 2006-05-18 14:16:56 UTC (rev 1472) </span><span class="lines">@@ -37,6 +37,7 @@ </span><span class="cx"> assert list(self.res[:10]) == self.orig[:10] </span><span class="cx"> assert list(self.res[10:40:3]) == self.orig[10:40:3] </span><span class="cx"> assert list(self.res[-5:]) == self.orig[-5:] </span><ins>+ assert self.res[10:20][5] == self.orig[10:20][5] </ins><span class="cx"> </span><span class="cx"> def test_aggregate(self): </span><span class="cx"> assert self.res.count() == 100 </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-18 14:15:52
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1471] sqlalchemy/branches/schema/test: c.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1471</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-18 09:15:34 -0500 (Thu, 18 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>c. martinez' fix to nested slices</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemymodsselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschematestselectresultspy">sqlalchemy/branches/schema/test/selectresults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyextselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py (1470 => 1471)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-05-18 00:12:10 UTC (rev 1470) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-05-18 14:15:34 UTC (rev 1471) </span><span class="lines">@@ -66,11 +66,11 @@ </span><span class="cx"> else: </span><span class="cx"> res = self.clone() </span><span class="cx"> if start is not None and stop is not None: </span><del>- res._ops.update(dict(offset=start, limit=stop-start)) </del><ins>+ res._ops.update(dict(offset=self._ops.get('offset', 0)+start, limit=stop-start)) </ins><span class="cx"> elif start is None and stop is not None: </span><span class="cx"> res._ops.update(dict(limit=stop)) </span><span class="cx"> elif start is not None and stop is None: </span><del>- res._ops.update(dict(offset=start)) </del><ins>+ res._ops.update(dict(offset=self._ops.get('offset', 0)+start)) </ins><span class="cx"> if item.step is not None: </span><span class="cx"> return list(res)[None:None:item.step] </span><span class="cx"> else: </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemymodsselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py (1470 => 1471)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-05-18 00:12:10 UTC (rev 1470) +++ sqlalchemy/branches/schema/lib/sqlalchemy/mods/selectresults.py 2006-05-18 14:15:34 UTC (rev 1471) </span><span class="lines">@@ -1,6 +1,7 @@ </span><span class="cx"> from sqlalchemy.ext.selectresults import * </span><ins>+from sqlalchemy.orm.mapper import global_extensions </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> def install_plugin(): </span><del>- orm.global_extensions.append(SelectResultsExt) </del><ins>+ global_extensions.append(SelectResultsExt) </ins><span class="cx"> install_plugin() </span></span></pre></div> <a id="sqlalchemybranchesschematestselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/selectresults.py (1470 => 1471)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/selectresults.py 2006-05-18 00:12:10 UTC (rev 1470) +++ sqlalchemy/branches/schema/test/selectresults.py 2006-05-18 14:15:34 UTC (rev 1471) </span><span class="lines">@@ -46,6 +46,7 @@ </span><span class="cx"> assert list(self.res[:10]) == self.orig[:10] </span><span class="cx"> assert list(self.res[10:40:3]) == self.orig[10:40:3] </span><span class="cx"> assert list(self.res[-5:]) == self.orig[-5:] </span><ins>+ assert self.res[10:20][5] == self.orig[10:20][5] </ins><span class="cx"> </span><span class="cx"> def test_aggregate(self): </span><span class="cx"> assert self.res.count() == 100 </span></span></pre> </div> </div> </body> </html> |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1470] sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py: restored "connection" member, readonly, to Transaction, fixes TLTransaction's access to it</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1470</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-17 19:12:10 -0500 (Wed, 17 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>restored "connection" member, readonly, to Transaction, fixes TLTransaction's access to it</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyenginebasepy">sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py (1469 => 1470)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-17 15:32:05 UTC (rev 1469) +++ sqlalchemy/branches/schema/lib/sqlalchemy/engine/base.py 2006-05-18 00:12:10 UTC (rev 1470) </span><span class="lines">@@ -335,6 +335,7 @@ </span><span class="cx"> self.__is_active = True </span><span class="cx"> if self.__parent is self: </span><span class="cx"> self.__connection._begin_impl() </span><ins>+ connection = property(lambda s:s.__connection, doc="The Connection object referenced by this Transaction") </ins><span class="cx"> def rollback(self): </span><span class="cx"> if not self.__parent.__is_active: </span><span class="cx"> raise exceptions.InvalidRequestError("This transaction is inactive") </span></span></pre> </div> </div> </body> </html> |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1469] sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py: adjustment to incoming Select statements on polymorphic_union, to "aliasize" them automatically</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1469</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-17 10:32:05 -0500 (Wed, 17 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>adjustment to incoming Select statements on polymorphic_union, to "aliasize" them automatically</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormutilpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py (1468 => 1469)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-05-17 01:16:16 UTC (rev 1468) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/util.py 2006-05-17 15:32:05 UTC (rev 1469) </span><span class="lines">@@ -24,7 +24,15 @@ </span><span class="cx"> def polymorphic_union(table_map, typecolname, aliasname='p_union'): </span><span class="cx"> colnames = sets.Set() </span><span class="cx"> colnamemaps = {} </span><del>- for table in table_map.values(): </del><ins>+ + for key in table_map.keys(): + table = table_map[key] + + # mysql doesnt like selecting from a select; make it an alias of the select + if isinstance(table, sql.Select): + table = table.alias() + table_map[key] = table + </ins><span class="cx"> m = {} </span><span class="cx"> for c in table.c: </span><span class="cx"> colnames.add(c.name) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-17 01:16:24
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1468] sqlalchemy/branches/schema/doc/build/content/plugins.txt: edits</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1468</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-16 20:16:16 -0500 (Tue, 16 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>edits</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentpluginstxt">sqlalchemy/branches/schema/doc/build/content/plugins.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentpluginstxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/plugins.txt (1467 => 1468)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-17 01:09:24 UTC (rev 1467) +++ sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-17 01:16:16 UTC (rev 1468) </span><span class="lines">@@ -147,8 +147,13 @@ </span><span class="cx"> </span><span class="cx"> ### SelectResults </span><span class="cx"> </span><del>- {python} - res = SelectResults(mapper, table.c.column == "something") </del><ins>+SelectResults gives generator-like behavior to the results returned from the `select` and `select_by` method of `Query`. It supports three modes of operation; per-query, per-mapper, and per-application. + + {python title="SelectResults with a Query Object"} + from sqlalchemy.ext.selectresults import SelectResults + + query = session.query(MyClass) + res = SelectResults(query, table.c.column == "something") </ins><span class="cx"> res = res.order_by([table.c.column]) #add an order clause </span><span class="cx"> </span><span class="cx"> for x in res[:10]: # Fetch and print the top ten instances </span><span class="lines">@@ -160,6 +165,23 @@ </span><span class="cx"> # and column == "something" </span><span class="cx"> print res.filter(table.c.column2 > 42).count() </span><span class="cx"> </span><ins>+ +Per mapper: + + {python title="SelectResults with a Mapper Object"} + from sqlalchemy.ext.selectresults import SelectResultsExt + mapper(MyClass, mytable, extension=SelectResultsExt()) + session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7] + +Or across an application via the `selectresults` mod: + + {python title="SelectResults via mod"} + import sqlalchemy.mods.selectresults + + mapper(MyClass, mytable) + session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7] + + </ins><span class="cx"> ### LegacySession </span><span class="cx"> </span><span class="cx"> (this plugin probably doesnt even work right now) </span><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-17 01:09:42
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1467] sqlalchemy/branches/schema/test: doc edits , slight tweak to join_to allowing the keyname of a relation or column property</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1467</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-16 20:09:24 -0500 (Tue, 16 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>doc edits , slight tweak to join_to allowing the keyname of a relation or column property</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemadocbuildcontentdatamappingtxt">sqlalchemy/branches/schema/doc/build/content/datamapping.txt</a></li> <li><a href="#sqlalchemybranchesschemadocbuildcontentpluginstxt">sqlalchemy/branches/schema/doc/build/content/plugins.txt</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemadocbuildcontentdatamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/datamapping.txt (1466 => 1467)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-16 00:25:50 UTC (rev 1466) +++ sqlalchemy/branches/schema/doc/build/content/datamapping.txt 2006-05-17 01:09:24 UTC (rev 1467) </span><span class="lines">@@ -79,13 +79,17 @@ </span><span class="cx"> # get a query from a Session given a Mapper: </span><span class="cx"> query = session.query(usermapper) </span><span class="cx"> </span><del>- # select_by, using property names or column names as keys - # the keys are grouped together by an AND operator </del><ins>+ # select_by, which takes keyword arguments. the + # keyword arguments represent property names and the values + # represent values which will be compared via the = operator. + # the comparisons are joined together via "AND". </ins><span class="cx"> result = query.select_by(name='john', street='123 green street') </span><span class="cx"> </span><del>- # select_by can also combine SQL criterion with key/value properties - result = query.select_by(users.c.user_name=='john', - addresses.c.zip_code=='12345', street=='123 green street') </del><ins>+ # select_by can also combine ClauseElements with key/value properties. + # all ClauseElements and keyword-based criterion are combined together + # via "AND". + result = query.select_by(users_table.c.user_name=='john', + addresses_table.c.zip_code=='12345', street='123 green street') </ins><span class="cx"> </span><span class="cx"> # get_by, which takes the same arguments as select_by </span><span class="cx"> # returns a single scalar result or None if no results </span><span class="lines">@@ -106,17 +110,17 @@ </span><span class="cx"> myobj = query.get((27, 3, 'receipts')) </span><span class="cx"> </span><span class="cx"> # using a WHERE criterion </span><del>- result = query.select(or_(users.c.user_name == 'john', users.c.user_name=='fred')) </del><ins>+ result = query.select(or_(users_table.c.user_name == 'john', users_table.c.user_name=='fred')) </ins><span class="cx"> </span><span class="cx"> # using a WHERE criterion to get a scalar </span><del>- u = query.selectfirst(users.c.user_name=='john') </del><ins>+ u = query.selectfirst(users_table.c.user_name=='john') </ins><span class="cx"> </span><span class="cx"> # selectone() is a stricter version of selectfirst() which </span><span class="cx"> # will raise an exception if there is not exactly one row </span><del>- u = query.selectone(users.c.user_name=='john') </del><ins>+ u = query.selectone(users_table.c.user_name=='john') </ins><span class="cx"> </span><span class="cx"> # using a full select object </span><del>- result = query.select(users.select(users.c.user_name=='john')) </del><ins>+ result = query.select(users_table.select(users_table.c.user_name=='john')) </ins><span class="cx"> </span><span class="cx"> Some of the above examples above illustrate the usage of the mapper's Table object to provide the columns for a WHERE Clause. These columns are also accessible off of the mapped class directly. When a mapper is assigned to a class, it also attaches a special property accessor `c` to the class itself, which can be used just like the table metadata to access the columns of the table: </span><span class="cx"> </span><span class="lines">@@ -197,8 +201,12 @@ </span><span class="cx"> </span><span class="cx"> ### Defining and Using Relationships {@name=relations} </span><span class="cx"> </span><del>-So that covers how to map the columns in a table to an object, how to load objects, create new ones, and save changes. The next step is how to define an object's relationships to other database-persisted objects. This is done via the `relation` function provided by the mapper module. So with our User class, lets also define the User has having one or more mailing addresses. First, the table metadata: </del><ins>+So that covers how to map the columns in a table to an object, how to load objects, create new ones, and save changes. The next step is how to define an object's relationships to other database-persisted objects. This is done via the `relation` function provided by the `orm` module. </ins><span class="cx"> </span><ins>+#### One to Many {@name=onetomany} + +So with our User class, lets also define the User has having one or more mailing addresses. First, the table metadata: + </ins><span class="cx"> {python} </span><span class="cx"> from sqlalchemy import * </span><span class="cx"> </span><span class="lines">@@ -221,7 +229,7 @@ </span><span class="cx"> Column('zip', String(10)) </span><span class="cx"> ) </span><span class="cx"> </span><del>-Of importance here is the addresses table's definition of a *foreign key* relationship to the users table, relating the user_id column into a parent-child relationship. When a `Mapper` wants to indicate a relation of one object to another, th `ForeignKey` relationships are the default method by which the relationship is determined (options also exist to describe the relationships explicitly). </del><ins>+Of importance here is the addresses table's definition of a *foreign key* relationship to the users table, relating the user_id column into a parent-child relationship. When a `Mapper` wants to indicate a relation of one object to another, the `ForeignKey` relationships are the default method by which the relationship is determined (options also exist to describe the relationships explicitly). </ins><span class="cx"> </span><span class="cx"> So then lets define two classes, the familiar `User` class, as well as an `Address` class: </span><span class="cx"> </span><span class="lines">@@ -238,7 +246,7 @@ </span><span class="cx"> self.state = state </span><span class="cx"> self.zip = zip </span><span class="cx"> </span><del>-And then a Mapper that will define a relationship of the User and the Address classes to each other as well as their table metadata. We will add an additional mapper keyword argument `properties` which is a dictionary relating the name of an object property to a database relationship, in this case a `relation` object against a newly defined mapper for the Address class: </del><ins>+And then a `Mapper` that will define a relationship of the `User` and the `Address` classes to each other as well as their table metadata. We will add an additional mapper keyword argument `properties` which is a dictionary relating the names of class attributes to database relationships, in this case a `relation` object against a newly defined mapper for the Address class: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> mapper(Address, addresses_table) </span><span class="lines">@@ -250,7 +258,7 @@ </span><span class="cx"> Lets do some operations with these classes and see what happens: </span><span class="cx"> </span><span class="cx"> {python} </span><del>- engine = create_engine('sqlite://filename=mydb') </del><ins>+ engine = create_engine('sqlite:///mydb.db') </ins><span class="cx"> metadata.create_all(engine) </span><span class="cx"> </span><span class="cx"> session = create_session(bind_to=engine) </span><span class="lines">@@ -268,9 +276,9 @@ </span><span class="cx"> INSERT INTO addresses (user_id, street, city, state, zip) VALUES (:user_id, :street, :city, :state, :zip) </span><span class="cx"> {'city': 'some other city', 'state': 'OK', 'street': '1 Park Place', 'user_id':1, 'zip': '83923'} </span><span class="cx"> </span><del>-A lot just happened there! The Mapper object figured out how to relate rows in the addresses table to the users table, and also upon flush had to determine the proper order in which to insert rows. After the insert, all the User and Address objects have all their new primary and foreign keys populated. </del><ins>+A lot just happened there! The `Mapper` figured out how to relate rows in the addresses table to the users table, and also upon flush had to determine the proper order in which to insert rows. After the insert, all the `User` and `Address` objects have their new primary and foreign key attributes populated. </ins><span class="cx"> </span><del>-Also notice that when we created a Mapper on the User class which defined an 'addresses' relation, the newly created User instance magically had an "addresses" attribute which behaved like a list. This list is in reality a property accessor function, which returns an instance of `sqlalchemy.util.HistoryArraySet`, which fulfills the full set of Python list accessors, but maintains a *unique* set of objects (based on their in-memory identity), and also tracks additions and deletions to the list: </del><ins>+Also notice that when we created a `Mapper` on the `User` class which defined an `addresses` relation, the newly created `User` instance magically had an "addresses" attribute which behaved like a list. This list is in reality a property function which returns an instance of `sqlalchemy.util.HistoryArraySet`. This object fulfills the full set of Python list accessors, but maintains a *unique* set of objects (based on their in-memory identity), and also tracks additions and deletions to the list: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> del u.addresses[1] </span><span class="lines">@@ -287,9 +295,9 @@ </span><span class="cx"> </span><span class="cx"> Note that when creating a relation with the `relation()` function, the target can either be a class, in which case the primary mapper for that class is used as the target, or a `Mapper` instance itself, as returned by the `mapper()` function. </span><span class="cx"> </span><del>-#### Useful Feature: Lifecycle Relations {@name=lifecycle} </del><ins>+#### Lifecycle Relations {@name=lifecycle} </ins><span class="cx"> </span><del>-So our one address that was removed from the list, was updated to have a user_id of `None`, and a new address object was inserted to correspond to the new Address added to the User. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the `cascade` parameter of `relation`: </del><ins>+In the previous example, a single address was removed from the `addresses` attribute of a `User` object, resulting in the corresponding database row being updated to have a user_id of `None`. But now, theres a mailing address with no user_id floating around in the database of no use to anyone. How can we avoid this ? This is acheived by using the `cascade` parameter of `relation`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> clear_mappers() # clear mappers from the previous example </span><span class="lines">@@ -309,13 +317,13 @@ </span><span class="cx"> DELETE FROM addresses WHERE addresses.address_id = :address_id </span><span class="cx"> [{'address_id': 2}] </span><span class="cx"> </span><del>-In this case, with `delete-orphan` set, the element that was removed from the addresses list was also removed from the database. The `delete-orphan` cascade rule indicates that the lifecycle of an `Address` object bounded by that of the `User`. </del><ins>+In this case, with the `delete-orphan` **cascade rule** set, the element that was removed from the addresses list was also removed from the database. Specifying `cascade="all, delete-orphan"` means that every persistence operation performed on the parent object will be *cascaded* to the child object or objects handled by the relation, and additionally that each child object cannot exist without being attached to a parent. Such a relationship indicates that the **lifecycle** of the `Address` objects are bounded by that of their parent `User` object. </ins><span class="cx"> </span><span class="cx"> Cascading is described fully in [unitofwork_cascade](rel:unitofwork_cascade). </span><span class="cx"> </span><del>-#### Useful Feature: Backreferences {@name=backreferences} </del><ins>+#### Backreferences {@name=backreferences} </ins><span class="cx"> </span><del>-By creating relations with the `backref` keyword, a bi-directional relationship can be created which will keep both ends of the relationship updated automatically, even without any database queries being executed. Below, the User mapper is created with an "addresses" property, and the corresponding Address mapper receives a "backreference" to the User object via the property name "user": </del><ins>+By creating relations with the `backref` keyword, a bi-directional relationship can be created which will keep both ends of the relationship updated automatically, independently of database operations. Below, the `User` mapper is created with an `addresses` property, and the corresponding `Address` mapper receives a "backreference" to the `User` object via the property name `user`: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> Address = mapper(Address, addresses_table) </span><span class="lines">@@ -340,31 +348,37 @@ </span><span class="cx"> >>> a1.user is user and a2.user is user </span><span class="cx"> True </span><span class="cx"> </span><del>-The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property is placed on the child mapper. The default arguments to this property can be overridden using the `backref()` function: </del><ins>+The backreference feature also works with many-to-many relationships, which are described later. When creating a backreference, a corresponding property (i.e. a second `relation()`) is placed on the child mapper. The default arguments to this property can be overridden using the `backref()` function: </ins><span class="cx"> </span><span class="cx"> {python} </span><del>- mapper(Address, addresseses) </del><ins>+ mapper(User, users_table) + mapper(Address, addresses_table, properties={ + 'user':relation(User, backref=backref('addresses', cascade="all, delete-orphan")) + }) </ins><span class="cx"> </span><del>- mapper(User, users, properties = { - 'addresses' : relation(Address, - backref=backref('user', lazy=False, cascade="all, delete-orphan") - ) - } - ) </del><span class="cx"> </span><del>-#### Selecting from Relationships: Lazy Load {@name=lazyload} </del><ins>+The `backref()` function is often used to set up a bi-directional one-to-one relationship. This is because the `relation()` function by default creates a "one-to-many" relationship when presented with a primary key/foreign key relationship, but the `backref()` function can redefine the `uselist` property to make it a scalar: </ins><span class="cx"> </span><del>-We've seen how the `relation` specifier affects the saving of an object and its child items, how does it affect selecting them? By default, the relation keyword indicates that the related property should be attached a *Lazy Loader* when instances of the parent object are loaded from the database; this is just a callable function that when accessed will invoke a second SQL query to load the child objects of the parent. </del><ins>+ {python} + mapper(User, users_table) + mapper(Address, addresses_table, properties={ + 'user' : relation(User, backref=backref('address', uselist=False)) + }) + + +### Selecting from Relationships {@name=selectrelations} + +We've seen how the `relation` specifier affects the saving of an object and its child items, how does it affect selecting them? By default, the relation keyword indicates that the related property should be attached a *lazy loader* when instances of the parent object are loaded from the database; this is just a callable function that when accessed will invoke a second SQL query to load the child objects of the parent. </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> # define a mapper </span><span class="cx"> mapper(User, users_table, properties = { </span><del>- 'addresses' : relation(mapper(Address, addresses_table), cascade="all,delete-orphan") </del><ins>+ 'addresses' : relation(mapper(Address, addresses_table)) </ins><span class="cx"> }) </span><span class="cx"> </span><span class="cx"> # select users where username is 'jane', get the first element of the list </span><span class="cx"> # this will incur a load operation for the parent table </span><del>- {sql}user = session.query(User).select_by(user_name='jane')[0] </del><ins>+ {sql}user = session.query(User).select(User.c.user_name=='jane')[0] </ins><span class="cx"> SELECT users.user_id AS users_user_id, </span><span class="cx"> users.user_name AS users_user_name, users.password AS users_password </span><span class="cx"> FROM users WHERE users.user_name = :users_user_name ORDER BY users.oid </span><span class="lines">@@ -382,9 +396,9 @@ </span><span class="cx"> </span><span class="cx"> print repr(a) </span><span class="cx"> </span><del>-##### Useful Feature: Creating Joins Across Relations {@name=relselectby} </del><ins>+#### Creating Joins Across Relations {@name=relselectby} </ins><span class="cx"> </span><del>-For mappers that have relationships, the `select_by` method of the Query object can create queries that include automatically created joins. Just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of one of its relationships: </del><ins>+For mappers that have relationships, the `select_by` method of the `Query` object can create queries that include automatically created joins. Just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of one of its relationships: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> {sql}l = session.query(User).select_by(street='123 Green Street') </span><span class="lines">@@ -404,16 +418,28 @@ </span><span class="cx"> Address.c.street=='123 Green Street') </span><span class="cx"> ) </span><span class="cx"> </span><del>-All keyword arguments sent to `select_by` are used to create query criterion. This means that familiar `select` keyword options like `order_by` and `limit` are not directly available. To enable these options with `select_by`, you can try the `SelectResults` extension which offers methods off the result of a `select` or `select_by` such as `order_by()` and array slicing functions that generate new queries. Or you can use `select` in conjunction with `join_to`. </del><ins>+All keyword arguments sent to `select_by` are used to create query criterion. This means that familiar `select` keyword options like `order_by` and `limit` are not directly available. To enable these options with `select_by`, you can try the [plugins_selectresults](rel:plugins_selectresults) extension which offers methods off the result of a `select` or `select_by` such as `order_by()` and array slicing functions that generate new queries. </ins><span class="cx"> </span><del>-The `join_to` method of `Query` is a component of the `select_by` operation, and is given a keyname in order to return a "join path" from the Query's mapper to the mapper which contains the property of the given name: </del><ins>+Also, `select_by` will *not* create joins derived from `Column`-based expressions (i.e. `ClauseElement` objects); the reason is that a `Column`-based expression may include many columns, and `select_by` has no way to know which columns in the expression correspond to properties and which don't (it also prefers not to dig into column expressions which may be very complex). The next section describes some ways to combine `Column` expressions with `select_by`'s auto-joining capabilities. </ins><span class="cx"> </span><ins>+#### More Granular Join Control Using join\_to, join\_via {@name=jointo} + +The `join_to` method of `Query` is a component of the `select_by` operation, and is given a keyname in order to return a "join path" from the Query's mapper to the mapper which is referenced by a `relation()` of the given name: + </ins><span class="cx"> {python} </span><span class="cx"> >>> q = session.query(User) </span><ins>+ >>> j = q.join_to('addresses') + >>> print j + users.user_id=addresses.user_id + +`join_to` can also be given the name of a column-based property, in which case it will locate a path to the nearest mapper which has that property as a column: + + {python} + >>> q = session.query(User) </ins><span class="cx"> >>> j = q.join_to('street') </span><span class="cx"> >>> print j </span><span class="cx"> users.user_id=addresses.user_id </span><del>- </del><ins>+ </ins><span class="cx"> Also available is the `join_via` function, which is similar to `join_to`, except instead of traversing through all properties to find a path to the given key, its given an explicit path to the target property: </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -422,19 +448,19 @@ </span><span class="cx"> >>> print j </span><span class="cx"> users.c.user_id==orders.c.user_id AND orders.c.item_id==items.c.item_id </span><span class="cx"> </span><del>-Expressions produced by `join_to` and `join_via` can be used with `select` to create query criterion: </del><ins>+Expressions produced by `join_to` and `join_via` can be used with `select` to create more complicated query criterion across multiple relations: </ins><span class="cx"> </span><span class="cx"> {python} </span><span class="cx"> >>> l = q.select( </span><span class="cx"> (addresses_table.c.street=='some address') & </span><span class="cx"> (items_table.c.item_name=='item #4') & </span><del>- q.join_to('address') & </del><ins>+ q.join_to('addresses') & </ins><span class="cx"> q.join_via(['orders', 'items']) </span><span class="cx"> ) </span><del>- - -#### Selecting from Relationships: Eager Load {@name=eagerload} </del><span class="cx"> </span><ins>+ +#### Eager Loading {@name=eagerload} + </ins><span class="cx"> With just a single parameter `lazy=False` specified to the relation object, the parent and child SQL queries can be joined together. </span><span class="cx"> </span><span class="cx"> {python} </span><span class="lines">@@ -479,7 +505,7 @@ </span><span class="cx"> </span><span class="cx"> The join implied by passing the "street" parameter is stated as an *additional* join between the `addresses` and `users` tables. Also, since the eager join is "aliasized", no name conflict occurs. </span><span class="cx"> </span><del>-#### Switching Lazy/Eager, No Load {@name=options} </del><ins>+#### Using Options to Change the Loading Strategy {@name=options} </ins><span class="cx"> </span><span class="cx"> The `options` method on the `Query` object provides an easy way to get alternate forms of a mapper query from an original one. The most common use of this feature is to change the "eager/lazy" loading behavior of a particular mapper, via the functions `eagerload()`, `lazyload()` and `noload()`: </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemadocbuildcontentpluginstxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/doc/build/content/plugins.txt (1466 => 1467)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-16 00:25:50 UTC (rev 1466) +++ sqlalchemy/branches/schema/doc/build/content/plugins.txt 2006-05-17 01:09:24 UTC (rev 1467) </span><span class="lines">@@ -30,6 +30,8 @@ </span><span class="cx"> user = User() </span><span class="cx"> </span><span class="cx"> session.flush() </span><ins>+ +#### get_session() Implemented on All Mappers </ins><span class="cx"> </span><span class="cx"> All `Mapper` objects constructed after the `threadlocal` import will receive a default `MapperExtension` which implements the `get_session()` method, returning the `Session` that is associated with the current thread by the global `SessionContext`. All newly constructed objects will automatically be attached to the `Session` corresponding to the current thread, i.e. they will skip the "transient" state and go right to "pending". </span><span class="cx"> </span><span class="lines">@@ -145,6 +147,19 @@ </span><span class="cx"> </span><span class="cx"> ### SelectResults </span><span class="cx"> </span><ins>+ {python} + res = SelectResults(mapper, table.c.column == "something") + res = res.order_by([table.c.column]) #add an order clause + + for x in res[:10]: # Fetch and print the top ten instances + print x.column2 + + x = list(res) # execute the query + + # Count how many instances that have column2 > 42 + # and column == "something" + print res.filter(table.c.column2 > 42).count() + </ins><span class="cx"> ### LegacySession </span><span class="cx"> </span><span class="cx"> (this plugin probably doesnt even work right now) </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1466 => 1467)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-16 00:25:50 UTC (rev 1466) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-05-17 01:09:24 UTC (rev 1467) </span><span class="lines">@@ -114,7 +114,10 @@ </span><span class="cx"> keys = [] </span><span class="cx"> def search_for_prop(mapper): </span><span class="cx"> if mapper.props.has_key(key): </span><del>- return mapper.props[key] </del><ins>+ prop = mapper.props[key] + if isinstance(prop, properties.PropertyLoader): + keys.insert(0, prop.key) + return prop </ins><span class="cx"> else: </span><span class="cx"> for prop in mapper.props.values(): </span><span class="cx"> if not isinstance(prop, properties.PropertyLoader): </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1466 => 1467)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-05-16 00:25:50 UTC (rev 1466) +++ sqlalchemy/branches/schema/test/mapper.py 2006-05-17 01:09:24 UTC (rev 1467) </span><span class="lines">@@ -230,6 +230,8 @@ </span><span class="cx"> l = q.select((orderitems.c.item_name=='item 4') & q.join_to('item_name')) </span><span class="cx"> self.assert_result(l, User, user_result[0]) </span><span class="cx"> </span><ins>+ l = q.select((orderitems.c.item_name=='item 4') & q.join_to('items')) + self.assert_result(l, User, user_result[0]) </ins><span class="cx"> </span><span class="cx"> def testorderby(self): </span><span class="cx"> # TODO: make a unit test out of these various combinations </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-16 00:32:21
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1466] sqlalchemy/trunk/doc/build/content/datamapping.txt: typo fix</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1466</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-15 19:25:50 -0500 (Mon, 15 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>typo fix</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdatamappingtxt">sqlalchemy/trunk/doc/build/content/datamapping.txt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdatamappingtxt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/datamapping.txt (1465 => 1466)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/datamapping.txt 2006-05-16 00:13:50 UTC (rev 1465) +++ sqlalchemy/trunk/doc/build/content/datamapping.txt 2006-05-16 00:25:50 UTC (rev 1466) </span><span class="lines">@@ -114,7 +114,7 @@ </span><span class="cx"> </span><span class="cx"> # select_by can also combine SQL criterion with key/value properties </span><span class="cx"> result = mapper.select_by(users.c.user_name=='john', </span><del>- addresses.c.zip_code=='12345, street='123 green street') </del><ins>+ addresses.c.zip_code=='12345', street='123 green street') </ins><span class="cx"> </span><span class="cx"> # get_by, which takes the same arguments as select_by </span><span class="cx"> # returns a single scalar result or None if no results </span></span></pre> </div> </div> </body> </html> |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1465] sqlalchemy/branches/schema/test: fixes to keep decorated __init__ method on the class local to the classes correct mapper, some selectresults fixes, unittests</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1465</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-15 19:13:50 -0500 (Mon, 15 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>fixes to keep decorated __init__ method on the class local to the classes correct mapper, some selectresults fixes, unittests</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextselectresultspy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyextselectresultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py (1464 => 1465)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-05-15 23:48:45 UTC (rev 1464) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/selectresults.py 2006-05-16 00:13:50 UTC (rev 1465) </span><span class="lines">@@ -5,7 +5,7 @@ </span><span class="cx"> </span><span class="cx"> class SelectResultsExt(orm.MapperExtension): </span><span class="cx"> def select_by(self, query, *args, **params): </span><del>- return SelectResults(query, query._by_clause(*args, **params)) </del><ins>+ return SelectResults(query, query.join_by(*args, **params)) </ins><span class="cx"> def select(self, query, arg=None, **kwargs): </span><span class="cx"> if arg is not None and isinstance(arg, sql.Selectable): </span><span class="cx"> return orm.EXT_PASS </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1464 => 1465)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-15 23:48:45 UTC (rev 1464) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-05-16 00:13:50 UTC (rev 1465) </span><span class="lines">@@ -322,10 +322,8 @@ </span><span class="cx"> instance._entity_name = self.entity_name </span><span class="cx"> </span><span class="cx"> def _init_class(self): </span><del>- """sets up our classes' overridden __init__ method, this mappers hash key as its - '_mapper' property, and our columns as its 'c' property. if the class already had a - mapper, the old __init__ method is kept the same.""" - ext = self.extension </del><ins>+ """decorates the __init__ method on the mapped class to include auto-session attachment logic, + and assocites this Mapper with its class via the mapper_registry.""" </ins><span class="cx"> oldinit = self.class_.__init__ </span><span class="cx"> def init(self, *args, **kwargs): </span><span class="cx"> self._entity_name = kwargs.pop('_sa_entity_name', None) </span><span class="lines">@@ -337,15 +335,19 @@ </span><span class="cx"> if kwargs.has_key('_sa_session'): </span><span class="cx"> session = kwargs.pop('_sa_session') </span><span class="cx"> else: </span><del>- session = ext.get_session() - if session is EXT_PASS: </del><ins>+ # works for whatever mapper the class is associated with + mapper = mapper_registry.get(ClassKey(self.__class__, self._entity_name)) + if mapper is not None: + session = mapper.extension.get_session() + if session is EXT_PASS: + session = None + else: </ins><span class="cx"> session = None </span><span class="cx"> if session is not None: </span><span class="cx"> session._register_new(self) </span><span class="cx"> if oldinit is not None: </span><span class="cx"> oldinit(self, *args, **kwargs) </span><del>- # override oldinit, insuring that its not already one of our - # own modified inits </del><ins>+ # override oldinit, insuring that its not already a Mapper-decorated init method </ins><span class="cx"> if oldinit is None or not hasattr(oldinit, '_sa_mapper_init'): </span><span class="cx"> init._sa_mapper_init = True </span><span class="cx"> self.class_.__init__ = init </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1464 => 1465)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-05-15 23:48:45 UTC (rev 1464) +++ sqlalchemy/branches/schema/test/alltests.py 2006-05-16 00:13:50 UTC (rev 1465) </span><span class="lines">@@ -14,7 +14,6 @@ </span><span class="cx"> </span><span class="cx"> # connectivity, execution </span><span class="cx"> 'pool', </span><del>- 'engine', </del><span class="cx"> 'transaction', </span><span class="cx"> </span><span class="cx"> # schema/tables </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1464 => 1465)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-05-15 23:48:45 UTC (rev 1464) +++ sqlalchemy/branches/schema/test/mapper.py 2006-05-16 00:13:50 UTC (rev 1465) </span><span class="lines">@@ -84,13 +84,14 @@ </span><span class="cx"> u2 = s.get(User, 7) </span><span class="cx"> self.assert_(u is not u2) </span><span class="cx"> </span><del>- </del><span class="cx"> def testrefresh(self): </span><span class="cx"> mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) </span><span class="cx"> s = create_session() </span><span class="cx"> u = s.get(User, 7) </span><span class="cx"> u.user_name = 'foo' </span><span class="cx"> a = Address() </span><ins>+ import sqlalchemy.orm.session + assert sqlalchemy.orm.session.object_session(a) is None </ins><span class="cx"> u.addresses.append(a) </span><span class="cx"> </span><span class="cx"> self.assert_(a in u.addresses) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-15 23:48:55
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1464] sqlalchemy/branches/schema/test: rick morrisons CASE statement merged from trunk 1462:1463</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1464</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-15 18:48:45 -0500 (Mon, 15 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>rick morrisons CASE statement merged from trunk 1462:1463</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyansisqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyextsessioncontextpy">sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemysqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemybranchesschematestalltestspy">sqlalchemy/branches/schema/test/alltests.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemybranchesschematestcase_statementpy">sqlalchemy/branches/schema/test/case_statement.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py (1463 => 1464)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-05-15 23:47:07 UTC (rev 1463) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ansisql.py 2006-05-15 23:48:45 UTC (rev 1464) </span><span class="lines">@@ -225,7 +225,13 @@ </span><span class="cx"> </span><span class="cx"> def apply_function_parens(self, func): </span><span class="cx"> return func.name.upper() not in ANSI_FUNCS or len(func.clauses) > 0 </span><del>- </del><ins>+ + def visit_calculatedclause(self, list): + if list.parens: + self.strings[list] = "(" + string.join([self.get_str(c) for c in list.clauses], ' ') + ")" + else: + self.strings[list] = string.join([self.get_str(c) for c in list.clauses], ' ') + </ins><span class="cx"> def visit_function(self, func): </span><span class="cx"> if len(self.select_stack): </span><span class="cx"> self.typemap.setdefault(func.name, func.type) </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyextsessioncontextpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py (1463 => 1464)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py 2006-05-15 23:47:07 UTC (rev 1463) +++ sqlalchemy/branches/schema/lib/sqlalchemy/ext/sessioncontext.py 2006-05-15 23:48:45 UTC (rev 1464) </span><span class="lines">@@ -1,6 +1,8 @@ </span><span class="cx"> from sqlalchemy.util import ScopedRegistry </span><span class="cx"> from sqlalchemy.orm.mapper import MapperExtension </span><span class="cx"> </span><ins>+__all__ = ['SessionContext', 'SessionContextExt'] + </ins><span class="cx"> class SessionContext(object): </span><span class="cx"> """A simple wrapper for ScopedRegistry that provides a "current" property </span><span class="cx"> which can be used to get, set, or remove the session in the current scope. </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/sql.py (1463 => 1464)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-15 23:47:07 UTC (rev 1463) +++ sqlalchemy/branches/schema/lib/sqlalchemy/sql.py 2006-05-15 23:48:45 UTC (rev 1464) </span><span class="lines">@@ -1,4 +1,3 @@ </span><del>-# sql.py </del><span class="cx"> # Copyright (C) 2005,2006 Michael Bayer mi...@zz... </span><span class="cx"> # </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="lines">@@ -11,7 +10,7 @@ </span><span class="cx"> import string, re, random, sets </span><span class="cx"> types = __import__('types') </span><span class="cx"> </span><del>-__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </del><ins>+__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'case', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </ins><span class="cx"> </span><span class="cx"> def desc(column): </span><span class="cx"> """returns a descending ORDER BY clause element, e.g.: </span><span class="lines">@@ -130,6 +129,17 @@ </span><span class="cx"> """ returns BETWEEN predicate clause (clausetest BETWEEN clauseleft AND clauseright) """ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><span class="cx"> between = between_ </span><ins>+ +def case(whens, value=None, else_=None): + """ SQL CASE statement -- whens are a sequence of pairs to be translated into "when / then" clauses; + optional [value] for simple case statements, and [else_] for case defaults """ + whenlist = [CompoundClause(None, 'WHEN', c, 'THEN', r) for (c,r) in whens] + if else_: + whenlist.append(CompoundClause(None, 'ELSE', else_)) + cc = CalculatedClause(None, 'CASE', value, *whenlist + ['END']) + for c in cc.clauses: + c.parens = False + return cc </ins><span class="cx"> </span><span class="cx"> def cast(clause, totype, **kwargs): </span><span class="cx"> """ returns CAST function CAST(clause AS totype) </span><span class="lines">@@ -297,6 +307,7 @@ </span><span class="cx"> def visit_join(self, join):pass </span><span class="cx"> def visit_null(self, null):pass </span><span class="cx"> def visit_clauselist(self, list):pass </span><ins>+ def visit_calculatedclause(self, calcclause):pass </ins><span class="cx"> def visit_function(self, func):pass </span><span class="cx"> def visit_label(self, label):pass </span><span class="cx"> def visit_typeclause(self, typeclause):pass </span><span class="lines">@@ -875,9 +886,42 @@ </span><span class="cx"> return self.operator == other.operator </span><span class="cx"> else: </span><span class="cx"> return False </span><ins>+ +class CalculatedClause(ClauseList, ColumnElement): + """ describes a calculated SQL expression that has a type, like CASE. extends ColumnElement to + provide column-level comparison operators. """ + def __init__(self, name, *clauses, **kwargs): + self.name = name + self.type = sqltypes.to_instance(kwargs.get('type', None)) + self._engine = kwargs.get('engine', None) + ClauseList.__init__(self, *clauses) + key = property(lambda self:self.name or "_calc_") + def _process_from_dict(self, data, asfrom): + super(CalculatedClause, self)._process_from_dict(data, asfrom) + # this helps a Select object get the engine from us + data.setdefault(self, self) + def copy_container(self): + clauses = [clause.copy_container() for clause in self.clauses] + return CalculatedClause(type=self.type, engine=self._engine, *clauses) + def accept_visitor(self, visitor): + for c in self.clauses: + c.accept_visitor(visitor) + visitor.visit_calculatedclause(self) + def _bind_param(self, obj): + return BindParamClause(self.name, obj, type=self.type) + def select(self): + return select([self]) + def scalar(self): + return select([self]).scalar() + def execute(self): + return select([self]).execute() + def _compare_type(self, obj): + return self.type + </ins><span class="cx"> </span><del>-class Function(ClauseList, ColumnElement): - """describes a SQL function. extends ClauseList to provide comparison operators.""" </del><ins>+class Function(CalculatedClause): + """describes a SQL function. extends CalculatedClause turn the "clauselist" into function + arguments, also adds a "packagenames" argument""" </ins><span class="cx"> def __init__(self, name, *clauses, **kwargs): </span><span class="cx"> self.name = name </span><span class="cx"> self.type = sqltypes.to_instance(kwargs.get('type', None)) </span><span class="lines">@@ -892,10 +936,6 @@ </span><span class="cx"> else: </span><span class="cx"> clause = BindParamClause(self.name, clause, shortname=self.name, type=None) </span><span class="cx"> self.clauses.append(clause) </span><del>- def _process_from_dict(self, data, asfrom): - super(Function, self)._process_from_dict(data, asfrom) - # this helps a Select object get the engine from us - data.setdefault(self, self) </del><span class="cx"> def copy_container(self): </span><span class="cx"> clauses = [clause.copy_container() for clause in self.clauses] </span><span class="cx"> return Function(self.name, type=self.type, packagenames=self.packagenames, engine=self._engine, *clauses) </span><span class="lines">@@ -903,17 +943,8 @@ </span><span class="cx"> for c in self.clauses: </span><span class="cx"> c.accept_visitor(visitor) </span><span class="cx"> visitor.visit_function(self) </span><del>- def _bind_param(self, obj): - return BindParamClause(self.name, obj, shortname=self.name, type=self.type) - def select(self): - return select([self]) - def scalar(self): - return select([self]).scalar() - def execute(self): - return select([self]).execute() - def _compare_type(self, obj): - return self.type </del><span class="cx"> </span><ins>+ </ins><span class="cx"> class FunctionGenerator(object): </span><span class="cx"> """generates Function objects based on getattr calls""" </span><span class="cx"> def __init__(self, engine=None): </span></span></pre></div> <a id="sqlalchemybranchesschematestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/alltests.py (1463 => 1464)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/alltests.py 2006-05-15 23:47:07 UTC (rev 1463) +++ sqlalchemy/branches/schema/test/alltests.py 2006-05-15 23:48:45 UTC (rev 1464) </span><span class="lines">@@ -25,6 +25,7 @@ </span><span class="cx"> # SQL syntax </span><span class="cx"> 'select', </span><span class="cx"> 'selectable', </span><ins>+ 'case_statement', </ins><span class="cx"> </span><span class="cx"> # assorted round-trip tests </span><span class="cx"> 'query', </span></span></pre></div> <a id="sqlalchemybranchesschematestcase_statementpyfromrev1463sqlalchemytrunktestcase_statementpy"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema/test/case_statement.py (from rev 1463, sqlalchemy/trunk/test/case_statement.py) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-05-15 23:47:19
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1463] sqlalchemy/trunk/test: rick morrison's CASE statement + unit test</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1463</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-15 18:47:07 -0500 (Mon, 15 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>rick morrison's CASE statement + unit test</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestalltestspy">sqlalchemy/trunk/test/alltests.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestcase_statementpy">sqlalchemy/trunk/test/case_statement.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1462 => 1463)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-05-15 22:47:41 UTC (rev 1462) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-05-15 23:47:07 UTC (rev 1463) </span><span class="lines">@@ -224,7 +224,13 @@ </span><span class="cx"> </span><span class="cx"> def apply_function_parens(self, func): </span><span class="cx"> return func.name.upper() not in ANSI_FUNCS or len(func.clauses) > 0 </span><del>- </del><ins>+ + def visit_calculatedclause(self, list): + if list.parens: + self.strings[list] = "(" + string.join([self.get_str(c) for c in list.clauses], ' ') + ")" + else: + self.strings[list] = string.join([self.get_str(c) for c in list.clauses], ' ') + </ins><span class="cx"> def visit_function(self, func): </span><span class="cx"> if len(self.select_stack): </span><span class="cx"> self.typemap.setdefault(func.name, func.type) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1462 => 1463)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-05-15 22:47:41 UTC (rev 1462) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-05-15 23:47:07 UTC (rev 1463) </span><span class="lines">@@ -1,4 +1,3 @@ </span><del>-# sql.py </del><span class="cx"> # Copyright (C) 2005,2006 Michael Bayer mi...@zz... </span><span class="cx"> # </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="lines">@@ -13,7 +12,7 @@ </span><span class="cx"> import string, re, random </span><span class="cx"> types = __import__('types') </span><span class="cx"> </span><del>-__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </del><ins>+__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'case', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists'] </ins><span class="cx"> </span><span class="cx"> def desc(column): </span><span class="cx"> """returns a descending ORDER BY clause element, e.g.: </span><span class="lines">@@ -132,6 +131,17 @@ </span><span class="cx"> """ returns BETWEEN predicate clause (clausetest BETWEEN clauseleft AND clauseright) """ </span><span class="cx"> return BooleanExpression(ctest, and_(cleft, cright), 'BETWEEN') </span><span class="cx"> between = between_ </span><ins>+ +def case(whens, value=None, else_=None): + """ SQL CASE statement -- whens are a sequence of pairs to be translated into "when / then" clauses; + optional [value] for simple case statements, and [else_] for case defaults """ + whenlist = [CompoundClause(None, 'WHEN', c, 'THEN', r) for (c,r) in whens] + if else_: + whenlist.append(CompoundClause(None, 'ELSE', else_)) + cc = CalculatedClause(None, 'CASE', value, *whenlist + ['END']) + for c in cc.clauses: + c.parens = False + return cc </ins><span class="cx"> </span><span class="cx"> def cast(clause, totype, **kwargs): </span><span class="cx"> """ returns CAST function CAST(clause AS totype) </span><span class="lines">@@ -295,6 +305,7 @@ </span><span class="cx"> def visit_join(self, join):pass </span><span class="cx"> def visit_null(self, null):pass </span><span class="cx"> def visit_clauselist(self, list):pass </span><ins>+ def visit_calculatedclause(self, calcclause):pass </ins><span class="cx"> def visit_function(self, func):pass </span><span class="cx"> def visit_label(self, label):pass </span><span class="cx"> def visit_typeclause(self, typeclause):pass </span><span class="lines">@@ -831,9 +842,42 @@ </span><span class="cx"> return self.operator == other.operator </span><span class="cx"> else: </span><span class="cx"> return False </span><ins>+ +class CalculatedClause(ClauseList, ColumnElement): + """ describes a calculated SQL expression that has a type, like CASE. extends ColumnElement to + provide column-level comparison operators. """ + def __init__(self, name, *clauses, **kwargs): + self.name = name + self.type = sqltypes.to_instance(kwargs.get('type', None)) + self._engine = kwargs.get('engine', None) + ClauseList.__init__(self, *clauses) + key = property(lambda self:self.name or "_calc_") + def _process_from_dict(self, data, asfrom): + super(CalculatedClause, self)._process_from_dict(data, asfrom) + # this helps a Select object get the engine from us + data.setdefault(self, self) + def copy_container(self): + clauses = [clause.copy_container() for clause in self.clauses] + return CalculatedClause(type=self.type, engine=self._engine, *clauses) + def accept_visitor(self, visitor): + for c in self.clauses: + c.accept_visitor(visitor) + visitor.visit_calculatedclause(self) + def _bind_param(self, obj): + return BindParamClause(self.name, obj, type=self.type) + def select(self): + return select([self]) + def scalar(self): + return select([self]).scalar() + def execute(self): + return select([self]).execute() + def _compare_type(self, obj): + return self.type + </ins><span class="cx"> </span><del>-class Function(ClauseList, ColumnElement): - """describes a SQL function. extends ClauseList to provide comparison operators.""" </del><ins>+class Function(CalculatedClause): + """describes a SQL function. extends CalculatedClause turn the "clauselist" into function + arguments, also adds a "packagenames" argument""" </ins><span class="cx"> def __init__(self, name, *clauses, **kwargs): </span><span class="cx"> self.name = name </span><span class="cx"> self.type = sqltypes.to_instance(kwargs.get('type', None)) </span><span class="lines">@@ -848,10 +892,6 @@ </span><span class="cx"> else: </span><span class="cx"> clause = BindParamClause(self.name, clause, shortname=self.name, type=None) </span><span class="cx"> self.clauses.append(clause) </span><del>- def _process_from_dict(self, data, asfrom): - super(Function, self)._process_from_dict(data, asfrom) - # this helps a Select object get the engine from us - data.setdefault(self, self) </del><span class="cx"> def copy_container(self): </span><span class="cx"> clauses = [clause.copy_container() for clause in self.clauses] </span><span class="cx"> return Function(self.name, type=self.type, packagenames=self.packagenames, engine=self._engine, *clauses) </span><span class="lines">@@ -859,17 +899,8 @@ </span><span class="cx"> for c in self.clauses: </span><span class="cx"> c.accept_visitor(visitor) </span><span class="cx"> visitor.visit_function(self) </span><del>- def _bind_param(self, obj): - return BindParamClause(self.name, obj, shortname=self.name, type=self.type) - def select(self): - return select([self]) - def scalar(self): - return select([self]).scalar() - def execute(self): - return select([self]).execute() - def _compare_type(self, obj): - return self.type </del><span class="cx"> </span><ins>+ </ins><span class="cx"> class FunctionGenerator(object): </span><span class="cx"> """generates Function objects based on getattr calls""" </span><span class="cx"> def __init__(self, engine=None): </span></span></pre></div> <a id="sqlalchemytrunktestalltestspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/alltests.py (1462 => 1463)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/alltests.py 2006-05-15 22:47:41 UTC (rev 1462) +++ sqlalchemy/trunk/test/alltests.py 2006-05-15 23:47:07 UTC (rev 1463) </span><span class="lines">@@ -24,6 +24,7 @@ </span><span class="cx"> # SQL syntax </span><span class="cx"> 'select', </span><span class="cx"> 'selectable', </span><ins>+ 'case_statement', </ins><span class="cx"> </span><span class="cx"> # assorted round-trip tests </span><span class="cx"> 'query', </span></span></pre></div> <a id="sqlalchemytrunktestcase_statementpy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/case_statement.py (1462 => 1463)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/case_statement.py 2006-05-15 22:47:41 UTC (rev 1462) +++ sqlalchemy/trunk/test/case_statement.py 2006-05-15 23:47:07 UTC (rev 1463) </span><span class="lines">@@ -0,0 +1,59 @@ </span><ins>+import sys +import testbase +from sqlalchemy import * + + +class CaseTest(testbase.PersistTest): + + def setUpAll(self): + global info_table + info_table = Table('infos', testbase.db, + Column('pk', Integer, primary_key=True), + Column('info', String)) + + info_table.create() + + info_table.insert().execute( + {'pk':1, 'info':'pk_1_data'}, + {'pk':2, 'info':'pk_2_data'}, + {'pk':3, 'info':'pk_3_data'}, + {'pk':4, 'info':'pk_4_data'}, + {'pk':5, 'info':'pk_5_data'}) + def tearDownAll(self): + info_table.drop() + + def testcase(self): + inner = select([case([[info_table.c.pk < 3, literal('lessthan3', type=String)], + [info_table.c.pk >= 3, literal('gt3', type=String)]]).label('x'), + info_table.c.pk, info_table.c.info], from_obj=[info_table]).alias('q_inner') + + inner_result = inner.execute().fetchall() + + # Outputs: + # lessthan3 1 pk_1_data + # lessthan3 2 pk_2_data + # gt3 3 pk_3_data + # gt3 4 pk_4_data + # gt3 5 pk_5_data + assert inner_result == [ + ('lessthan3', 1, 'pk_1_data'), + ('lessthan3', 2, 'pk_2_data'), + ('gt3', 3, 'pk_3_data'), + ('gt3', 4, 'pk_4_data'), + ('gt3', 5, 'pk_5_data'), + ] + + outer = select([inner]) + + outer_result = outer.execute().fetchall() + + assert outer_result == [ + ('lessthan3', 1, 'pk_1_data'), + ('lessthan3', 2, 'pk_2_data'), + ('gt3', 3, 'pk_3_data'), + ('gt3', 4, 'pk_4_data'), + ('gt3', 5, 'pk_5_data'), + ] + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |