[Sqlalchemy-commits] [1057] sqlalchemy/trunk/test: more work on cycles, fleshed out tests for post_u
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-02-27 01:05: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>[1057] sqlalchemy/trunk/test: more work on cycles, fleshed out tests for post_update, fix to the delete phase of a one-to-many post update</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1057</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-02-26 19:05:01 -0600 (Sun, 26 Feb 2006)</dd> </dl> <h3>Log Message</h3> <pre>more work on cycles, fleshed out tests for post_update, fix to the delete phase of a one-to-many post update closes [ticket:67]</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunktestcyclespy">sqlalchemy/trunk/test/cycles.py</a></li> <li><a href="#sqlalchemytrunktesttestbasepy">sqlalchemy/trunk/test/testbase.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1056 => 1057)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-02-26 23:40:15 UTC (rev 1056) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-02-27 01:05:01 UTC (rev 1057) </span><span class="lines">@@ -458,7 +458,7 @@ </span><span class="cx"> elif self.direction == PropertyLoader.ONETOMANY and delete: </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><del>- if self.private: </del><ins>+ if self.private and not self.post_update: </ins><span class="cx"> # if we are privately managed, then all our objects should </span><span class="cx"> # have been marked as "todelete" already and no attribute adjustment is needed </span><span class="cx"> return </span></span></pre></div> <a id="sqlalchemytrunktestcyclespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/cycles.py (1056 => 1057)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/cycles.py 2006-02-26 23:40:15 UTC (rev 1056) +++ sqlalchemy/trunk/test/cycles.py 2006-02-27 01:05:01 UTC (rev 1057) </span><span class="lines">@@ -19,7 +19,7 @@ </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "%s(%s)" % (self.__class__.__name__, repr(self.data)) </span><span class="cx"> </span><del>-class SelfCycleTest(AssertMixin): </del><ins>+class SelfReferentialTest(AssertMixin): </ins><span class="cx"> """tests a self-referential mapper, with an additional list of child objects.""" </span><span class="cx"> def setUpAll(self): </span><span class="cx"> testbase.db.tables.clear() </span><span class="lines">@@ -67,8 +67,8 @@ </span><span class="cx"> objectstore.delete(a) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><del>-class CycleTest(AssertMixin): - """tests two mappers with a bi-directional dependency""" </del><ins>+class BiDirectionalOneToManyTest(AssertMixin): + """tests two mappers with a one-to-many relation to each other.""" </ins><span class="cx"> def setUpAll(self): </span><span class="cx"> testbase.db.tables.clear() </span><span class="cx"> global t1 </span><span class="lines">@@ -112,8 +112,8 @@ </span><span class="cx"> b.c1s.append(c) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><del>-class CycleWDepsTest(AssertMixin): - """tests two mappers with a bi-directional dependency, and child objects on one of them""" </del><ins>+class BiDirectionalOneToManyTest2(AssertMixin): + """tests two mappers with a one-to-many relation to each other, with a second one-to-many on one of the mappers""" </ins><span class="cx"> def setUpAll(self): </span><span class="cx"> testbase.db.tables.clear() </span><span class="cx"> global t1 </span><span class="lines">@@ -178,25 +178,35 @@ </span><span class="cx"> objectstore.delete(c) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><del>-class CycleTest2(AssertMixin): </del><ins>+class OneToManyManyToOneTest(AssertMixin): + """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. + two tests will have a row for each item that is dependent on the other. without the "post_update" flag, such relationships + raise an exception when dependencies are sorted.""" </ins><span class="cx"> def setUpAll(self): </span><span class="cx"> testbase.db.tables.clear() </span><span class="cx"> global person </span><span class="cx"> global ball </span><ins>+ ball = Table('ball', db, + Column('id', Integer, Sequence('ball_id_seq', optional=True), primary_key=True), + Column('person_id', Integer), + ) </ins><span class="cx"> person = Table('person', db, </span><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><ins>+# Column('favoriteBall_id', Integer), </ins><span class="cx"> ) </span><del>- ball = Table('ball', db, - Column('id', Integer, Sequence('ball_id_seq', optional=True), primary_key=True), - Column('person_id', Integer), - ) </del><span class="cx"> </span><span class="cx"> ball.create() </span><span class="cx"> person.create() </span><ins>+# person.c.favoriteBall_id.append_item(ForeignKey('ball.id')) </ins><span class="cx"> ball.c.person_id.append_item(ForeignKey('person.id')) </span><span class="cx"> </span><ins>+ # make the test more complete for postgres + if db.engine.__module__.endswith('postgres'): + db.execute("alter table ball add constraint fk_ball_person foreign key (person_id) references person(id)", {}) </ins><span class="cx"> def tearDownAll(self): </span><ins>+ if db.engine.__module__.endswith('postgres'): + db.execute("alter table ball drop constraint fk_ball_person", {}) </ins><span class="cx"> person.drop() </span><span class="cx"> ball.drop() </span><span class="cx"> </span><span class="lines">@@ -228,8 +238,8 @@ </span><span class="cx"> p.balls.append(b) </span><span class="cx"> objectstore.commit() </span><span class="cx"> </span><del>- def testrowcycle(self): - """tests a cycle between two rows""" </del><ins>+ def testpostupdate_m2o(self): + """tests a cycle between two rows, with a post_update on the many-to-one""" </ins><span class="cx"> class Person(object): </span><span class="cx"> pass </span><span class="cx"> </span><span class="lines">@@ -252,12 +262,217 @@ </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>- objectstore.commit() - </del><ins>+ + self.assert_sql(db, lambda: objectstore.uow().commit(), [ + ( + "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)", + {'favoriteBall_id': None} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + lambda:{'person_id':p.id} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + lambda:{'person_id':p.id} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + lambda:{'person_id':p.id} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + lambda:{'person_id':p.id} + ), + ( + "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", + lambda:[{'favoriteBall_id':p.favorateBall.id,'person_id':p.id}] + ) + ], + with_sequences= [ + ( + "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)", + lambda:{'id':db.last_inserted_ids()[0], 'favoriteBall_id': None} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0],'person_id':p.id} + ), + # heres the post update + ( + "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", + lambda:[{'favoriteBall_id':p.favorateBall.id,'person_id':p.id}] + ) + ]) </ins><span class="cx"> objectstore.delete(p) </span><del>- objectstore.commit() </del><ins>+ self.assert_sql(db, lambda: objectstore.uow().commit(), [ + # heres the post update (which is a pre-update with deletes) + ( + "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id", + lambda:[{'person_id': p.id, 'favoriteBall_id': None}] + ), + ( + "DELETE FROM ball WHERE ball.id = :id", + None + # order cant be predicted, but something like: + #lambda:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}] + ), + ( + "DELETE FROM person WHERE person.id = :id", + lambda:[{'id': p.id}] + ) </ins><span class="cx"> </span><ins>+ + ]) </ins><span class="cx"> </span><ins>+ def testpostupdate_o2m(self): + """tests a cycle between two rows, with a post_update on the one-to-many""" + class Person(object): + pass + + class Ball(object): + pass + + Ball.mapper = mapper(Ball, ball) + Person.mapper = mapper(Person, person, properties= dict( + balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id, private=True, post_update=True), + favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id), + ) + ) + + print str(Person.mapper.props['balls'].primaryjoin) + + b = Ball() + p = Person() + p.balls.append(b) + b2 = Ball() + p.balls.append(b2) + b3 = Ball() + p.balls.append(b3) + b4 = Ball() + p.balls.append(b4) + p.favorateBall = b +# objectstore.commit() + self.assert_sql(db, lambda: objectstore.uow().commit(), [ + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + {'person_id':None} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + {'person_id':None} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + {'person_id':None} + ), + ( + "INSERT INTO ball (person_id) VALUES (:person_id)", + {'person_id':None} + ), + ( + "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)", + lambda:{'favoriteBall_id':b.id} + ), + # heres the post update on each one-to-many item + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b2.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b3.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b4.id}] + ), + ], + with_sequences=[ + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} + ), + ( + "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)", + lambda:{'id':db.last_inserted_ids()[0], 'person_id':None} + ), + ( + "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)", + lambda:{'id':db.last_inserted_ids()[0], 'favoriteBall_id':b.id} + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b2.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b3.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id':p.id,'ball_id':b4.id}] + ), + ]) + + objectstore.delete(p) + self.assert_sql(db, lambda: objectstore.uow().commit(), [ + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id': None, 'ball_id': b.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id': None, 'ball_id': b2.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id': None, 'ball_id': b3.id}] + ), + ( + "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", + lambda:[{'person_id': None, 'ball_id': b4.id}] + ), + ( + "DELETE FROM person WHERE person.id = :id", + lambda:[{'id':p.id}] + ), + ( + "DELETE FROM ball WHERE ball.id = :id", + None + # the order of deletion is not predictable, but its roughly: + # lambda:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}] + ) + ]) + </ins><span class="cx"> if __name__ == "__main__": </span><span class="cx"> testbase.main() </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktesttestbasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/testbase.py (1056 => 1057)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/testbase.py 2006-02-26 23:40:15 UTC (rev 1056) +++ sqlalchemy/trunk/test/testbase.py 2006-02-27 01:05:01 UTC (rev 1057) </span><span class="lines">@@ -143,7 +143,7 @@ </span><span class="cx"> </span><span class="cx"> query = self.convert_statement(query) </span><span class="cx"> </span><del>- self.unittest.assert_(statement == query and params == parameters, "Testing for query '%s' params %s, received '%s' with params %s" % (query, repr(params), statement, repr(parameters))) </del><ins>+ self.unittest.assert_(statement == query and (params is None or params == parameters), "Testing for query '%s' params %s, received '%s' with params %s" % (query, repr(params), statement, repr(parameters))) </ins><span class="cx"> self.sql_count += 1 </span><span class="cx"> return self.realexec(proxy, compiled, parameters, **kwargs) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |