diff options
Diffstat (limited to 'test/orm/cycles.py')
-rw-r--r-- | test/orm/cycles.py | 1244 |
1 files changed, 630 insertions, 614 deletions
diff --git a/test/orm/cycles.py b/test/orm/cycles.py index 7a6c3d31e..d43a9062c 100644 --- a/test/orm/cycles.py +++ b/test/orm/cycles.py @@ -1,97 +1,97 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy.orm import * -from testlib import * -from testlib.tables import * -from orm import _base - -""" -Tests cyclical mapper relationships. +"""Tests cyclical mapper relationships. We might want to try an automated generate of much of this, all combos of T1<->T2, with o2m or m2o between them, and a third T3 with o2m/m2o to one/both T1/T2. + """ +import testenv; testenv.configure_for_tests() +from testlib import testing +from testlib.sa import Table, Column, Integer, String, ForeignKey +from testlib.sa.orm import mapper, relation, backref, create_session +from testlib.testing import eq_ +from orm import _base + +class SelfReferentialTest(_base.MappedTest): + """A self-referential mapper with an additional list of child objects.""" + + def define_tables(self, metadata): + Table('t1', metadata, + Column('c1', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('parent_c1', Integer, ForeignKey('t1.c1')), + Column('data', String(20))) + Table('t2', metadata, + Column('c1', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('c1id', Integer, ForeignKey('t1.c1')), + Column('data', String(20))) + + def setup_classes(self): + class C1(_base.BasicEntity): + def __init__(self, data=None): + self.data = data -class Tester(object): - def __init__(self, data=None): - self.data = data - print repr(self) + " (%d)" % (id(self)) - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self.data)) - -class SelfReferentialTest(_base.ORMTest): - """tests a self-referential mapper, with an additional list of child objects.""" - def setUpAll(self): - global t1, t2, metadata - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('c1', Integer, Sequence('t1c1_id_seq', optional=True), primary_key=True), - Column('parent_c1', Integer, ForeignKey('t1.c1')), - Column('data', String(20)) - ) - t2 = Table('t2', metadata, - Column('c1', Integer, Sequence('t2c1_id_seq', optional=True), primary_key=True), - Column('c1id', Integer, ForeignKey('t1.c1')), - Column('data', String(20)) - ) - metadata.create_all() - def tearDownAll(self): - metadata.drop_all() - def setUp(self): - clear_mappers() + class C2(_base.BasicEntity): + def __init__(self, data=None): + self.data = data + @testing.resolve_artifact_names def testsingle(self): - class C1(Tester): - pass - m1 = mapper(C1, t1, properties = { + mapper(C1, t1, properties = { 'c1s':relation(C1, cascade="all"), - 'parent':relation(C1, primaryjoin=t1.c.parent_c1==t1.c.c1, remote_side=t1.c.c1, lazy=True, uselist=False) - }) + 'parent':relation(C1, + primaryjoin=t1.c.parent_c1 == t1.c.c1, + remote_side=t1.c.c1, + lazy=True, + uselist=False)}) a = C1('head c1') a.c1s.append(C1('another c1')) + sess = create_session( ) - sess.save(a) + sess.add(a) sess.flush() sess.delete(a) sess.flush() + @testing.resolve_artifact_names def testmanytooneonly(self): - """test that the circular dependency sort can assemble a many-to-one dependency processor - when only the object on the "many" side is actually in the list of modified objects. - this requires that the circular sort add the other side of the relation into the UOWTransaction - so that the dependency operation can be tacked onto it. + """ + + test that the circular dependency sort can assemble a many-to-one + dependency processor when only the object on the "many" side is + actually in the list of modified objects. this requires that the + circular sort add the other side of the relation into the + UOWTransaction so that the dependency operation can be tacked onto it. + + This also affects inheritance relationships since they rely upon + circular sort as well. - This also affects inheritance relationships since they rely upon circular sort as well. """ - class C1(Tester): - pass mapper(C1, t1, properties={ - 'parent':relation(C1, primaryjoin=t1.c.parent_c1==t1.c.c1, remote_side=t1.c.c1) - }) - sess = create_session() + 'parent':relation(C1, + primaryjoin=t1.c.parent_c1 == t1.c.c1, + remote_side=t1.c.c1)}) + c1 = C1() - sess.save(c1) + + sess = create_session() + sess.add(c1) sess.flush() sess.clear() c1 = sess.query(C1).get(c1.c1) c2 = C1() c2.parent = c1 - sess.save(c2) + sess.add(c2) sess.flush() assert c2.parent_c1==c1.c1 + @testing.resolve_artifact_names def testcycle(self): - class C1(Tester): - pass - class C2(Tester): - pass - - m1 = mapper(C1, t1, properties = { + mapper(C1, t1, properties = { 'c1s' : relation(C1, cascade="all"), - 'c2s' : relation(mapper(C2, t2), cascade="all, delete-orphan") - }) + 'c2s' : relation(mapper(C2, t2), cascade="all, delete-orphan")}) a = C1('head c1') a.c1s.append(C1('child1')) @@ -101,110 +101,110 @@ class SelfReferentialTest(_base.ORMTest): a.c1s[1].c2s.append(C2('child2 data1')) a.c1s[1].c2s.append(C2('child2 data2')) sess = create_session( ) - sess.save(a) + sess.add(a) sess.flush() sess.delete(a) sess.flush() -class SelfReferentialNoPKTest(TestBase, AssertsExecutionResults): - """test self-referential relationship that joins on a column other than the primary key column""" - def setUpAll(self): - global table, meta - meta = MetaData(testing.db) - table = Table('item', meta, + +class SelfReferentialNoPKTest(_base.MappedTest): + """A self-referential relationship that joins on a column other than the primary key column""" + + def define_tables(self, metadata): + Table('item', metadata, Column('id', Integer, primary_key=True), Column('uuid', String(32), unique=True, nullable=False), - Column('parent_uuid', String(32), ForeignKey('item.uuid'), nullable=True), - ) - meta.create_all() - def tearDown(self): - table.delete().execute() - def tearDownAll(self): - meta.drop_all() - def testbasic(self): - class TT(object): + Column('parent_uuid', String(32), ForeignKey('item.uuid'), + nullable=True)) + + def setup_classes(self): + class TT(_base.BasicEntity): def __init__(self): self.uuid = hex(id(self)) - mapper(TT, table, properties={'children':relation(TT, remote_side=[table.c.parent_uuid], backref=backref('parent', remote_side=[table.c.uuid]))}) - s = create_session() + + @testing.resolve_artifact_names + def setup_mappers(self): + mapper(TT, item, properties={ + 'children': relation( + TT, + remote_side=[item.c.parent_uuid], + backref=backref('parent', remote_side=[item.c.uuid]))}) + + @testing.resolve_artifact_names + def testbasic(self): t1 = TT() t1.children.append(TT()) t1.children.append(TT()) - s.save(t1) + + s = create_session() + s.add(t1) s.flush() s.clear() t = s.query(TT).filter_by(id=t1.id).one() - assert t.children[0].parent_uuid == t1.uuid + eq_(t.children[0].parent_uuid, t1.uuid) + + @testing.resolve_artifact_names def testlazyclause(self): - class TT(object): - def __init__(self): - self.uuid = hex(id(self)) - mapper(TT, table, properties={'children':relation(TT, remote_side=[table.c.parent_uuid], backref=backref('parent', remote_side=[table.c.uuid]))}) s = create_session() t1 = TT() t2 = TT() t1.children.append(t2) - s.save(t1) + s.add(t1) s.flush() s.clear() t = s.query(TT).filter_by(id=t2.id).one() - assert t.uuid == t2.uuid - assert t.parent.uuid == t1.uuid - -class InheritTestOne(_base.ORMTest): - def setUpAll(self): - global parent, child1, child2, meta - meta = MetaData(testing.db) - parent = Table("parent", meta, + eq_(t.uuid, t2.uuid) + eq_(t.parent.uuid, t1.uuid) + + +class InheritTestOne(_base.MappedTest): + def define_tables(self, metadata): + Table("parent", metadata, Column("id", Integer, primary_key=True), Column("parent_data", String(50)), - Column("type", String(10)) - ) - - child1 = Table("child1", meta, - Column("id", Integer, ForeignKey("parent.id"), primary_key=True), - Column("child1_data", String(50)) - ) - - child2 = Table("child2", meta, - Column("id", Integer, ForeignKey("parent.id"), primary_key=True), - Column("child1_id", Integer, ForeignKey("child1.id"), nullable=False), - Column("child2_data", String(50)) - ) - meta.create_all() - - def tearDownAll(self): - meta.drop_all() - - def testmanytooneonly(self): - """test similar to SelfReferentialTest.testmanytooneonly""" - - class Parent(object): + Column("type", String(10))) + + Table("child1", metadata, + Column("id", Integer, ForeignKey("parent.id"), + primary_key=True), + Column("child1_data", String(50))) + + Table("child2", metadata, + Column("id", Integer, ForeignKey("parent.id"), + primary_key=True), + Column("child1_id", Integer, ForeignKey("child1.id"), + nullable=False), + Column("child2_data", String(50))) + + def setup_classes(self): + class Parent(_base.BasicEntity): pass - mapper(Parent, parent) - class Child1(Parent): pass - mapper(Child1, child1, inherits=Parent) - class Child2(Parent): pass - mapper(Child2, child2, properties={ - "child1": relation(Child1, - primaryjoin=child2.c.child1_id==child1.c.id, - ) - },inherits=Parent) + @testing.resolve_artifact_names + def setup_mappers(self): + mapper(Parent, parent) + mapper(Child1, child1, inherits=Parent) + mapper(Child2, child2, inherits=Parent, properties=dict( + child1=relation(Child1, + primaryjoin=child2.c.child1_id == child1.c.id))) + + @testing.resolve_artifact_names + def testmanytooneonly(self): + """test similar to SelfReferentialTest.testmanytooneonly""" session = create_session() c1 = Child1() c1.child1_data = "qwerty" - session.save(c1) + session.add(c1) session.flush() session.clear() @@ -212,133 +212,129 @@ class InheritTestOne(_base.ORMTest): c2 = Child2() c2.child1 = c1 c2.child2_data = "asdfgh" - session.save(c2) + session.add(c2) + # the flush will fail if the UOW does not set up a many-to-one DP - # attached to a task corresponding to c1, since "child1_id" is not nullable + # attached to a task corresponding to c1, since "child1_id" is not + # nullable session.flush() + class InheritTestTwo(_base.MappedTest): - """the fix in BiDirectionalManyToOneTest raised this issue, regarding - the 'circular sort' containing UOWTasks that were still polymorphic, which could - create duplicate entries in the final sort - + """ + + The fix in BiDirectionalManyToOneTest raised this issue, regarding the + 'circular sort' containing UOWTasks that were still polymorphic, which + could create duplicate entries in the final sort + """ def define_tables(self, metadata): - global a, b, c - a = Table('a', metadata, + Table('a', metadata, Column('id', Integer, primary_key=True), Column('data', String(30)), - Column('cid', Integer, ForeignKey('c.id')), - ) + Column('cid', Integer, ForeignKey('c.id'))) - b = Table('b', metadata, + Table('b', metadata, Column('id', Integer, ForeignKey("a.id"), primary_key=True), - Column('data', String(30)), - ) + Column('data', String(30))) - c = Table('c', metadata, + Table('c', metadata, Column('id', Integer, primary_key=True), Column('data', String(30)), - Column('aid', Integer, ForeignKey('a.id', use_alter=True, name="foo")), - ) - - def test_flush(self): - class A(object):pass - class B(A):pass - class C(object):pass + Column('aid', Integer, + ForeignKey('a.id', use_alter=True, name="foo"))) + + def setup_classes(self): + class A(_base.BasicEntity): + pass + + class B(A): + pass + + class C(_base.BasicEntity): + pass + @testing.resolve_artifact_names + def test_flush(self): mapper(A, a, properties={ - 'cs':relation(C, primaryjoin=a.c.cid==c.c.id) - }) + 'cs':relation(C, primaryjoin=a.c.cid==c.c.id)}) + + mapper(B, b, inherits=A, inherit_condition=b.c.id == a.c.id) - mapper(B, b, inherits=A, inherit_condition=b.c.id==a.c.id, properties={ - }) mapper(C, c, properties={ - 'arel':relation(A, primaryjoin=a.c.id==c.c.aid) - }) + 'arel':relation(A, primaryjoin=a.c.id == c.c.aid)}) sess = create_session() bobj = B() - sess.save(bobj) + sess.add(bobj) cobj = C() - sess.save(cobj) + sess.add(cobj) sess.flush() class BiDirectionalManyToOneTest(_base.MappedTest): + def define_tables(self, metadata): - global t1, t2, t3, t4 - t1 = Table('t1', metadata, + Table('t1', metadata, Column('id', Integer, primary_key=True), Column('data', String(30)), - Column('t2id', Integer, ForeignKey('t2.id')) - ) - t2 = Table('t2', metadata, + Column('t2id', Integer, ForeignKey('t2.id'))) + Table('t2', metadata, Column('id', Integer, primary_key=True), Column('data', String(30)), - Column('t1id', Integer, ForeignKey('t1.id', use_alter=True, name="foo_fk")) - ) - t3 = Table('t3', metadata, + Column('t1id', Integer, + ForeignKey('t1.id', use_alter=True, name="foo_fk"))) + Table('t3', metadata, Column('id', Integer, primary_key=True), Column('data', String(30)), Column('t1id', Integer, ForeignKey('t1.id'), nullable=False), - Column('t2id', Integer, ForeignKey('t2.id'), nullable=False), - ) + Column('t2id', Integer, ForeignKey('t2.id'), nullable=False)) - def test_reflush(self): - class T1(object):pass - class T2(object):pass - class T3(object):pass + def setup_classes(self): + class T1(_base.BasicEntity): + pass + class T2(_base.BasicEntity): + pass + class T3(_base.BasicEntity): + pass + @testing.resolve_artifact_names + def setup_mappers(self): mapper(T1, t1, properties={ - 't2':relation(T2, primaryjoin=t1.c.t2id==t2.c.id) - }) + 't2':relation(T2, primaryjoin=t1.c.t2id == t2.c.id)}) mapper(T2, t2, properties={ - 't1':relation(T1, primaryjoin=t2.c.t1id==t1.c.id) - }) + 't1':relation(T1, primaryjoin=t2.c.t1id == t1.c.id)}) mapper(T3, t3, properties={ 't1':relation(T1), - 't2':relation(T2) - }) + 't2':relation(T2)}) + @testing.resolve_artifact_names + def test_reflush(self): o1 = T1() o1.t2 = T2() sess = create_session() - sess.save(o1) + sess.add(o1) sess.flush() - # the bug here is that the dependency sort comes up with T1/T2 in a cycle, but there - # are no T1/T2 objects to be saved. therefore no "cyclical subtree" gets generated, - # and one or the other of T1/T2 gets lost, and processors on T3 dont fire off. - # the test will then fail because the FK's on T3 are not nullable. + # the bug here is that the dependency sort comes up with T1/T2 in a + # cycle, but there are no T1/T2 objects to be saved. therefore no + # "cyclical subtree" gets generated, and one or the other of T1/T2 + # gets lost, and processors on T3 dont fire off. the test will then + # fail because the FK's on T3 are not nullable. o3 = T3() o3.t1 = o1 o3.t2 = o1.t2 - sess.save(o3) + sess.add(o3) sess.flush() + @testing.resolve_artifact_names def test_reflush_2(self): - """a variant on test_reflush()""" - class T1(object):pass - class T2(object):pass - class T3(object):pass - - mapper(T1, t1, properties={ - 't2':relation(T2, primaryjoin=t1.c.t2id==t2.c.id) - }) - mapper(T2, t2, properties={ - 't1':relation(T1, primaryjoin=t2.c.t1id==t1.c.id) - }) - mapper(T3, t3, properties={ - 't1':relation(T1), - 't2':relation(T2) - }) - + """A variant on test_reflush()""" o1 = T1() o1.t2 = T2() sess = create_session() - sess.save(o1) + sess.add(o1) sess.flush() # in this case, T1, T2, and T3 tasks will all be in the cyclical @@ -347,47 +343,53 @@ class BiDirectionalManyToOneTest(_base.MappedTest): # as well. o1a = T1() o2a = T2() - sess.save(o1a) - sess.save(o2a) + sess.add(o1a) + sess.add(o2a) o3b = T3() o3b.t1 = o1a o3b.t2 = o2a - sess.save(o3b) + sess.add(o3b) o3 = T3() o3.t1 = o1 o3.t2 = o1.t2 - sess.save(o3) + sess.add(o3) sess.flush() -class BiDirectionalOneToManyTest(TestBase, AssertsExecutionResults): + +class BiDirectionalOneToManyTest(_base.MappedTest): """tests two mappers with a one-to-many relation to each other.""" - def setUpAll(self): - global t1, t2, metadata - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('c1', Integer, Sequence('t1c1_id_seq', optional=True), primary_key=True), - Column('c2', Integer, ForeignKey('t2.c1')) - ) - t2 = Table('t2', metadata, - Column('c1', Integer, Sequence('t2c1_id_seq', optional=True), primary_key=True), - Column('c2', Integer, ForeignKey('t1.c1', use_alter=True, name='t1c1_fk')) - ) - metadata.create_all() - def tearDownAll(self): - metadata.drop_all() - def tearDown(self): - clear_mappers() + + def define_tables(self, metadata): + Table('t1', metadata, + Column('c1', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('c2', Integer, ForeignKey('t2.c1'))) + + Table('t2', metadata, + Column('c1', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('c2', Integer, + ForeignKey('t1.c1', use_alter=True, name='t1c1_fk'))) + + def setup_classes(self): + class C1(_base.BasicEntity): + pass + + class C2(_base.BasicEntity): + pass + + @testing.resolve_artifact_names def testcycle(self): - class C1(object):pass - class C2(object):pass - - m2 = mapper(C2, t2, properties={ - 'c1s': relation(C1, primaryjoin=t2.c.c1==t1.c.c2, uselist=True) - }) - m1 = mapper(C1, t1, properties = { - 'c2s' : relation(C2, primaryjoin=t1.c.c1==t2.c.c2, uselist=True) - }) + mapper(C2, t2, properties={ + 'c1s': relation(C1, + primaryjoin=t2.c.c1 == t1.c.c2, + uselist=True)}) + mapper(C1, t1, properties={ + 'c2s': relation(C2, + primaryjoin=t1.c.c1 == t2.c.c2, + uselist=True)}) + a = C1() b = C2() c = C1() @@ -398,49 +400,55 @@ class BiDirectionalOneToManyTest(TestBase, AssertsExecutionResults): d.c1s.append(c) b.c1s.append(c) sess = create_session() - [sess.save(x) for x in [a,b,c,d,e,f]] + sess.add_all((a, b, c, d, e, f)) sess.flush() -class BiDirectionalOneToManyTest2(_base.ORMTest): - """tests two mappers with a one-to-many relation to each other, with a second one-to-many on one of the mappers""" - def setUpAll(self): - global t1, t2, t3, metadata - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('c1', Integer, Sequence('t1c1_id_seq', optional=True), primary_key=True), - Column('c2', Integer, ForeignKey('t2.c1')), - ) - t2 = Table('t2', metadata, - Column('c1', Integer, Sequence('t2c1_id_seq', optional=True), primary_key=True), - Column('c2', Integer, ForeignKey('t1.c1', use_alter=True, name='t1c1_fq')), - ) - t3 = Table('t1_data', metadata, - Column('c1', Integer, Sequence('t1dc1_id_seq', optional=True), primary_key=True), - Column('t1id', Integer, ForeignKey('t1.c1')), - Column('data', String(20))) - metadata.create_all() - - def tearDown(self): - clear_mappers() - - def tearDownAll(self): - metadata.drop_all() - def testcycle(self): - class C1(object):pass - class C2(object):pass - class C1Data(object): - def __init__(self, data=None): - self.data = data +class BiDirectionalOneToManyTest2(_base.MappedTest): + """Two mappers with a one-to-many relation to each other, with a second one-to-many on one of the mappers""" - m2 = mapper(C2, t2, properties={ - 'c1s': relation(C1, primaryjoin=t2.c.c1==t1.c.c2, uselist=True) - }) - m1 = mapper(C1, t1, properties = { - 'c2s' : relation(C2, primaryjoin=t1.c.c1==t2.c.c2, uselist=True), - 'data' : relation(mapper(C1Data, t3)) - }) + def define_tables(self, metadata): + Table('t1', metadata, + Column('c1', Integer, primary_key=True), + Column('c2', Integer, ForeignKey('t2.c1')), + test_needs_autoincrement=True) + + Table('t2', metadata, + Column('c1', Integer, primary_key=True), + Column('c2', Integer, + ForeignKey('t1.c1', use_alter=True, name='t1c1_fq')), + test_needs_autoincrement=True) + + Table('t1_data', metadata, + Column('c1', Integer, primary_key=True), + Column('t1id', Integer, ForeignKey('t1.c1')), + Column('data', String(20)), + test_needs_autoincrement=True) + + def setup_classes(self): + class C1(_base.BasicEntity): + pass + class C2(_base.BasicEntity): + pass + + class C1Data(_base.BasicEntity): + pass + + @testing.resolve_artifact_names + def setup_mappers(self): + mapper(C2, t2, properties={ + 'c1s': relation(C1, + primaryjoin=t2.c.c1 == t1.c.c2, + uselist=True)}) + mapper(C1, t1, properties={ + 'c2s': relation(C2, + primaryjoin=t1.c.c1 == t2.c.c2, + uselist=True), + 'data': relation(mapper(C1Data, t1_data))}) + + @testing.resolve_artifact_names + def testcycle(self): a = C1() b = C2() c = C1() @@ -450,356 +458,364 @@ class BiDirectionalOneToManyTest2(_base.ORMTest): a.c2s.append(b) d.c1s.append(c) b.c1s.append(c) - a.data.append(C1Data('c1data1')) - a.data.append(C1Data('c1data2')) - c.data.append(C1Data('c1data3')) + a.data.append(C1Data(data='c1data1')) + a.data.append(C1Data(data='c1data2')) + c.data.append(C1Data(data='c1data3')) sess = create_session() - [sess.save(x) for x in [a,b,c,d,e,f]] + sess.add_all((a, b, c, d, e, f)) sess.flush() sess.delete(d) sess.delete(c) sess.flush() -class OneToManyManyToOneTest(_base.ORMTest): - """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.""" - def setUpAll(self): - global metadata - metadata = MetaData(testing.db) - global person - global ball - ball = Table('ball', metadata, - Column('id', Integer, Sequence('ball_id_seq', optional=True), primary_key=True), - Column('person_id', Integer, ForeignKey('person.id', use_alter=True, name='fk_person_id')), - Column('data', String(30)) - ) - person = Table('person', metadata, - Column('id', Integer, Sequence('person_id_seq', optional=True), primary_key=True), - Column('favorite_ball_id', Integer, ForeignKey('ball.id')), - Column('data', String(30)) - ) - - metadata.create_all() - - def tearDownAll(self): - metadata.drop_all() - - def tearDown(self): - clear_mappers() +class OneToManyManyToOneTest(_base.MappedTest): + """ - def testcycle(self): - """this test has a peculiar aspect in that it doesnt create as many dependent - relationships as the other tests, and revealed a small glitch in the circular dependency sorting. - - """ - class Person(object): + 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. + + """ + + def define_tables(self, metadata): + Table('ball', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('person_id', Integer, + ForeignKey('person.id', use_alter=True, name='fk_person_id')), + Column('data', String(30))) + + Table('person', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('favorite_ball_id', Integer, ForeignKey('ball.id')), + Column('data', String(30))) + + def setup_classes(self): + class Person(_base.BasicEntity): pass - class Ball(object): + class Ball(_base.BasicEntity): 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, remote_side=ball.c.person_id), - favorateBall = relation(Ball.mapper, primaryjoin=person.c.favorite_ball_id==ball.c.id, remote_side=ball.c.id), - ) - ) + @testing.resolve_artifact_names + def testcycle(self): + """ + This test has a peculiar aspect in that it doesnt create as many + dependent relationships as the other tests, and revealed a small + glitch in the circular dependency sorting. + + """ + mapper(Ball, ball) + mapper(Person, person, properties=dict( + balls=relation(Ball, + primaryjoin=ball.c.person_id == person.c.id, + remote_side=ball.c.person_id), + favorite=relation(Ball, + primaryjoin=person.c.favorite_ball_id == ball.c.id, + remote_side=ball.c.id))) b = Ball() p = Person() p.balls.append(b) sess = create_session() - sess.save(p) + sess.add(p) sess.flush() - - def testpostupdate_m2o(self): - """tests a cycle between two rows, with a post_update on the many-to-one""" - class Person(object): - def __init__(self, data): - self.data = data - class Ball(object): - def __init__(self, data): - self.data = data - - Ball.mapper = mapper(Ball, ball) - Person.mapper = mapper(Person, person, properties= dict( - balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, remote_side=ball.c.person_id, post_update=False, cascade="all, delete-orphan"), - favorateBall = relation(Ball.mapper, primaryjoin=person.c.favorite_ball_id==ball.c.id, remote_side=person.c.favorite_ball_id, post_update=True), - ) - ) - - b = Ball('some data') - p = Person('some data') + @testing.resolve_artifact_names + def testpostupdate_m2o(self): + """A cycle between two rows, with a post_update on the many-to-one""" + mapper(Ball, ball) + mapper(Person, person, properties=dict( + balls=relation(Ball, + primaryjoin=ball.c.person_id == person.c.id, + remote_side=ball.c.person_id, + post_update=False, + cascade="all, delete-orphan"), + favorite=relation(Ball, + primaryjoin=person.c.favorite_ball_id == ball.c.id, + remote_side=person.c.favorite_ball_id, + post_update=True))) + + b = Ball(data='some data') + p = Person(data='some data') p.balls.append(b) - p.balls.append(Ball('some data')) - p.balls.append(Ball('some data')) - p.balls.append(Ball('some data')) - p.favorateBall = b + p.balls.append(Ball(data='some data')) + p.balls.append(Ball(data='some data')) + p.balls.append(Ball(data='some data')) + p.favorite = b sess = create_session() - sess.save(b) - sess.save(p) - - self.assert_sql(testing.db, lambda: sess.flush(), [ - ( - "INSERT INTO person (favorite_ball_id, data) VALUES (:favorite_ball_id, :data)", - {'favorite_ball_id': None, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - lambda ctx:{'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - lambda ctx:{'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - lambda ctx:{'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - lambda ctx:{'person_id':p.id, 'data':'some data'} - ), - ( - "UPDATE person SET favorite_ball_id=:favorite_ball_id WHERE person.id = :person_id", - lambda ctx:{'favorite_ball_id':p.favorateBall.id,'person_id':p.id} - ) - ], - with_sequences= [ - ( - "INSERT INTO person (id, favorite_ball_id, data) VALUES (:id, :favorite_ball_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favorite_ball_id': None, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id, 'data':'some data'} - ), - # heres the post update - ( - "UPDATE person SET favorite_ball_id=:favorite_ball_id WHERE person.id = :person_id", - lambda ctx:{'favorite_ball_id':p.favorateBall.id,'person_id':p.id} - ) - ]) + sess.add(b) + sess.add(p) + + self.assert_sql(testing.db, sess.flush, [ + ("INSERT INTO person (favorite_ball_id, data) " + "VALUES (:favorite_ball_id, :data)", + {'favorite_ball_id': None, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + lambda ctx:{'person_id':p.id, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + lambda ctx:{'person_id':p.id, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + lambda ctx:{'person_id':p.id, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + lambda ctx:{'person_id':p.id, 'data':'some data'}), + + ("UPDATE person SET favorite_ball_id=:favorite_ball_id " + "WHERE person.id = :person_id", + lambda ctx:{'favorite_ball_id':p.favorite.id, 'person_id':p.id}) + ], + + with_sequences= [ + ("INSERT INTO person (id, favorite_ball_id, data) " + "VALUES (:id, :favorite_ball_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'favorite_ball_id': None, + 'data':'some data'}), + + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'person_id':p.id, + 'data':'some data'}), + + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'person_id':p.id, + 'data':'some data'}), + + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'person_id':p.id, + 'data':'some data'}), + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'person_id':p.id, + 'data':'some data'}), + # heres the post update + ("UPDATE person SET favorite_ball_id=:favorite_ball_id " + "WHERE person.id = :person_id", + lambda ctx: {'favorite_ball_id':p.favorite.id, 'person_id':p.id})]) + sess.delete(p) - self.assert_sql(testing.db, lambda: sess.flush(), [ + + self.assert_sql(testing.db, sess.flush, [ # heres the post update (which is a pre-update with deletes) - ( - "UPDATE person SET favorite_ball_id=:favorite_ball_id WHERE person.id = :person_id", - lambda ctx:{'person_id': p.id, 'favorite_ball_id': None} - ), - ( - "DELETE FROM ball WHERE ball.id = :id", - None - # order cant be predicted, but something like: - #lambda ctx:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}] - ), - ( - "DELETE FROM person WHERE person.id = :id", - lambda ctx:[{'id': p.id}] - ) - - - ]) + ("UPDATE person SET favorite_ball_id=:favorite_ball_id " + "WHERE person.id = :person_id", + lambda ctx: {'person_id': p.id, 'favorite_ball_id': None}), - def testpostupdate_o2m(self): - """tests a cycle between two rows, with a post_update on the one-to-many""" - class Person(object): - def __init__(self, data): - self.data = data + ("DELETE FROM ball WHERE ball.id = :id", + None), + # order cant be predicted, but something like: + #lambda ctx:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}]), - class Ball(object): - def __init__(self, data): - self.data = data + ("DELETE FROM person WHERE person.id = :id", + lambda ctx:[{'id': p.id}])]) - Ball.mapper = mapper(Ball, ball) - Person.mapper = mapper(Person, person, properties= dict( - balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, remote_side=ball.c.person_id, cascade="all, delete-orphan", post_update=True, backref='person'), - favorateBall = relation(Ball.mapper, primaryjoin=person.c.favorite_ball_id==ball.c.id, remote_side=person.c.favorite_ball_id), - ) - ) - b = Ball('some data') - p = Person('some data') + @testing.resolve_artifact_names + def testpostupdate_o2m(self): + """A cycle between two rows, with a post_update on the one-to-many""" + + mapper(Ball, ball) + mapper(Person, person, properties=dict( + balls=relation(Ball, + primaryjoin=ball.c.person_id == person.c.id, + remote_side=ball.c.person_id, + cascade="all, delete-orphan", + post_update=True, + backref='person'), + favorite=relation(Ball, + primaryjoin=person.c.favorite_ball_id == ball.c.id, + remote_side=person.c.favorite_ball_id))) + + b = Ball(data='some data') + p = Person(data='some data') p.balls.append(b) - b2 = Ball('some data') + b2 = Ball(data='some data') p.balls.append(b2) - b3 = Ball('some data') + b3 = Ball(data='some data') p.balls.append(b3) - b4 = Ball('some data') + b4 = Ball(data='some data') p.balls.append(b4) - p.favorateBall = b + p.favorite = b sess = create_session() - [sess.save(x) for x in [b,p,b2,b3,b4]] - - self.assert_sql(testing.db, lambda: sess.flush(), [ - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - {'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - {'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - {'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (person_id, data) VALUES (:person_id, :data)", - {'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO person (favorite_ball_id, data) VALUES (:favorite_ball_id, :data)", - lambda ctx:{'favorite_ball_id':b.id, 'data':'some data'} - ), - # heres the post update on each one-to-many item - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b2.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b3.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b4.id} - ), - ], - with_sequences=[ - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO ball (id, person_id, data) VALUES (:id, :person_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None, 'data':'some data'} - ), - ( - "INSERT INTO person (id, favorite_ball_id, data) VALUES (:id, :favorite_ball_id, :data)", - lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favorite_ball_id':b.id, 'data':'some data'} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b2.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b3.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id':p.id,'ball_id':b4.id} - ), - ]) + sess.add_all((b,p,b2,b3,b4)) + + self.assert_sql(testing.db, sess.flush, [ + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + {'person_id':None, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + {'person_id':None, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + {'person_id':None, 'data':'some data'}), + + ("INSERT INTO ball (person_id, data) " + "VALUES (:person_id, :data)", + {'person_id':None, 'data':'some data'}), + + ("INSERT INTO person (favorite_ball_id, data) " + "VALUES (:favorite_ball_id, :data)", + lambda ctx:{'favorite_ball_id':b.id, 'data':'some data'}), + + # heres the post update on each one-to-many item + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b2.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b3.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b4.id})], + + with_sequences=[ + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx: {'id':ctx.last_inserted_ids()[0], + 'person_id':None, + 'data':'some data'}), + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx:{'id':ctx.last_inserted_ids()[0], + 'person_id':None, + 'data':'some data'}), + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx:{'id':ctx.last_inserted_ids()[0], + 'person_id':None, + 'data':'some data'}), + ("INSERT INTO ball (id, person_id, data) " + "VALUES (:id, :person_id, :data)", + lambda ctx:{'id':ctx.last_inserted_ids()[0], + 'person_id':None, + 'data':'some data'}), + ("INSERT INTO person (id, favorite_ball_id, data) " + "VALUES (:id, :favorite_ball_id, :data)", + lambda ctx:{'id':ctx.last_inserted_ids()[0], + 'favorite_ball_id':b.id, + 'data':'some data'}), + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b2.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b3.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id':p.id,'ball_id':b4.id})]) sess.delete(p) - self.assert_sql(testing.db, lambda: sess.flush(), [ - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id': None, 'ball_id': b.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id': None, 'ball_id': b2.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id': None, 'ball_id': b3.id} - ), - ( - "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id", - lambda ctx:{'person_id': None, 'ball_id': b4.id} - ), - ( - "DELETE FROM person WHERE person.id = :id", - lambda ctx:[{'id':p.id}] - ), - ( - "DELETE FROM ball WHERE ball.id = :id", - lambda ctx:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}] - ) - ]) - -class SelfReferentialPostUpdateTest(_base.ORMTest): - """test using post_update on a single self-referential mapper""" - def setUpAll(self): - global metadata, node_table - metadata = MetaData(testing.db) - node_table = Table('node', metadata, - Column('id', Integer, Sequence('nodeid_id_seq', optional=True), primary_key=True), - Column('path', String(50), nullable=False), - Column('parent_id', Integer, ForeignKey('node.id'), nullable=True), - Column('prev_sibling_id', Integer, ForeignKey('node.id'), nullable=True), - Column('next_sibling_id', Integer, ForeignKey('node.id'), nullable=True) - ) - node_table.create() - def tearDownAll(self): - node_table.drop() + self.assert_sql(testing.db, sess.flush, [ + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id': None, 'ball_id': b.id}), - def testbasic(self): - """test that post_update only fires off when needed. + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id': None, 'ball_id': b2.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id': None, 'ball_id': b3.id}), + + ("UPDATE ball SET person_id=:person_id " + "WHERE ball.id = :ball_id", + lambda ctx:{'person_id': None, 'ball_id': b4.id}), + + ("DELETE FROM person WHERE person.id = :id", + lambda ctx:[{'id':p.id}]), + + ("DELETE FROM ball WHERE ball.id = :id", + lambda ctx:[{'id': b.id}, + {'id': b2.id}, + {'id': b3.id}, + {'id': b4.id}])]) - this test case used to produce many superfluous update statements, particularly upon delete""" - class Node(object): + +class SelfReferentialPostUpdateTest(_base.MappedTest): + """Post_update on a single self-referential mapper""" + + def define_tables(self, metadata): + Table('node', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('path', String(50), nullable=False), + Column('parent_id', Integer, + ForeignKey('node.id'), nullable=True), + Column('prev_sibling_id', Integer, + ForeignKey('node.id'), nullable=True), + Column('next_sibling_id', Integer, + ForeignKey('node.id'), nullable=True)) + + def setup_classes(self): + class Node(_base.BasicEntity): def __init__(self, path=''): self.path = path - n_mapper = mapper(Node, node_table, properties={ + @testing.resolve_artifact_names + def testbasic(self): + """Post_update only fires off when needed. + + This test case used to produce many superfluous update statements, + particularly upon delete + + """ + + mapper(Node, node, properties={ 'children': relation( Node, - primaryjoin=node_table.c.id==node_table.c.parent_id, + primaryjoin=node.c.id==node.c.parent_id, lazy=True, cascade="all", - backref=backref("parent", primaryjoin=node_table.c.parent_id==node_table.c.id, remote_side=node_table.c.id) - ), + backref=backref("parent", + primaryjoin=node.c.parent_id == node.c.id, + remote_side=node.c.id)), 'prev_sibling': relation( Node, - primaryjoin=node_table.c.prev_sibling_id==node_table.c.id, - remote_side=node_table.c.id, + primaryjoin=node.c.prev_sibling_id==node.c.id, + remote_side=node.c.id, lazy=True, - uselist=False - ), + uselist=False), 'next_sibling': relation( Node, - primaryjoin=node_table.c.next_sibling_id==node_table.c.id, - remote_side=node_table.c.id, + primaryjoin=node.c.next_sibling_id==node.c.id, + remote_side=node.c.id, lazy=True, uselist=False, - post_update=True - ) - }) + post_update=True)}) session = create_session() @@ -831,72 +847,72 @@ class SelfReferentialPostUpdateTest(_base.ORMTest): assert(about.prev_sibling is None) append_child(root, stories) append_child(root, bruce) - session.save(root) + session.add(root) session.flush() remove_child(root, cats) # pre-trigger lazy loader on 'cats' to make the test easier cats.children self.assert_sql(testing.db, lambda: session.flush(), [ - ( - "UPDATE node SET prev_sibling_id=:prev_sibling_id WHERE node.id = :node_id", - lambda ctx:{'prev_sibling_id':about.id, 'node_id':stories.id} - ), - ( - "UPDATE node SET next_sibling_id=:next_sibling_id WHERE node.id = :node_id", - lambda ctx:{'next_sibling_id':stories.id, 'node_id':about.id} - ), - ( - "UPDATE node SET next_sibling_id=:next_sibling_id WHERE node.id = :node_id", - lambda ctx:{'next_sibling_id':None, 'node_id':cats.id} - ), - ( - "DELETE FROM node WHERE node.id = :id", - lambda ctx:[{'id':cats.id}] - ), - ]) - -class SelfReferentialPostUpdateTest2(_base.ORMTest): - def setUpAll(self): - global metadata, a_table - metadata = MetaData(testing.db) - a_table = Table("a", metadata, - Column("id", Integer(), primary_key=True), - Column("fui", String(128)), - Column("b", Integer(), ForeignKey("a.id")), - ) - a_table.create() - def tearDownAll(self): - a_table.drop() + ("UPDATE node SET prev_sibling_id=:prev_sibling_id " + "WHERE node.id = :node_id", + lambda ctx:{'prev_sibling_id':about.id, 'node_id':stories.id}), + + ("UPDATE node SET next_sibling_id=:next_sibling_id " + "WHERE node.id = :node_id", + lambda ctx:{'next_sibling_id':stories.id, 'node_id':about.id}), + + ("UPDATE node SET next_sibling_id=:next_sibling_id " + "WHERE node.id = :node_id", + lambda ctx:{'next_sibling_id':None, 'node_id':cats.id}), + ("DELETE FROM node WHERE node.id = :id", + lambda ctx:[{'id':cats.id}])]) + + +class SelfReferentialPostUpdateTest2(_base.MappedTest): + + def define_tables(self, metadata): + Table("a_table", metadata, + Column("id", Integer(), primary_key=True), + Column("fui", String(128)), + Column("b", Integer(), ForeignKey("a_table.id"))) + def setup_classes(self): + class A(_base.BasicEntity): + pass + + @testing.resolve_artifact_names def testbasic(self): - """test that post_update remembers to be involved in update operations as well, - since it replaces the normal dependency processing completely [ticket:413]""" - class a(object): - def __init__(self, fui): - self.fui = fui + """ + Test that post_update remembers to be involved in update operations as + well, since it replaces the normal dependency processing completely + [ticket:413] + + """ - mapper(a, a_table, properties={ - 'foo': relation(a, remote_side=[a_table.c.id], post_update=True), - }) + mapper(A, a_table, properties={ + 'foo': relation(A, + remote_side=[a_table.c.id], + post_update=True)}) session = create_session() - f1 = a("f1") - session.save(f1) + f1 = A(fui="f1") + session.add(f1) session.flush() - f2 = a("f2") - f2.foo = f1 + f2 = A(fui="f2", foo=f1) + # at this point f1 is already inserted. but we need post_update # to fire off anyway - session.save(f2) + session.add(f2) session.flush() - session.clear() - f1 = session.query(a).get(f1.id) - f2 = session.query(a).get(f2.id) + + f1 = session.query(A).get(f1.id) + f2 = session.query(A).get(f2.id) assert f2.foo is f1 + if __name__ == "__main__": testenv.main() |