diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-02-07 21:57:30 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-02-07 21:57:30 +0000 |
commit | 2dba55cb27cf52fb41533ff0cf85096db38b5d9d (patch) | |
tree | 297276ed8f29151484f8c9433fa1f8addb59074c | |
parent | 1c751e3ddb4768b2ba916da9bc1354887077889a (diff) | |
download | sqlalchemy-2dba55cb27cf52fb41533ff0cf85096db38b5d9d.tar.gz |
- When flushing partial sets of objects using session.flush([somelist]),
pending objects which remain pending after the operation won't
inadvertently be added as persistent. [ticket:1306]
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 2 | ||||
-rw-r--r-- | test/orm/cascade.py | 111 |
3 files changed, 116 insertions, 1 deletions
@@ -17,6 +17,10 @@ CHANGES like mapper(A.join(B)) -> relation-> mapper(B) can be determined. + - When flushing partial sets of objects using session.flush([somelist]), + pending objects which remain pending after the operation won't + inadvertently be added as persistent. [ticket:1306] + - sql - Fixed missing _label attribute on Function object, others when used in a select() with use_labels (such as when used diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index c756045a1..61c58b249 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -267,7 +267,7 @@ class UOWTransaction(object): for elem in self.elements: if elem.isdelete: self.session._remove_newly_deleted(elem.state) - else: + elif not elem.listonly: self.session._register_newly_persistent(elem.state) def _sort_dependencies(self): diff --git a/test/orm/cascade.py b/test/orm/cascade.py index 3345a5d8c..4c7d1f103 100644 --- a/test/orm/cascade.py +++ b/test/orm/cascade.py @@ -1176,5 +1176,116 @@ class CollectionAssignmentOrphanTest(_base.MappedTest): eq_(sess.query(A).get(a1.id), A(name='a1', bs=[B(name='b1'), B(name='b2'), B(name='b3')])) + +class PartialFlushTest(_base.MappedTest): + """test cascade behavior as it relates to object lists passed to flush(). + + """ + def define_tables(self, metadata): + Table("base", metadata, + Column("id", Integer, primary_key=True), + Column("descr", String) + ) + + Table("noninh_child", metadata, + Column('id', Integer, primary_key=True), + Column('base_id', Integer, ForeignKey('base.id')) + ) + + Table("parent", metadata, + Column("id", Integer, ForeignKey("base.id"), primary_key=True) + ) + Table("inh_child", metadata, + Column("id", Integer, ForeignKey("base.id"), primary_key=True), + Column("parent_id", Integer, ForeignKey("parent.id")) + ) + + + @testing.resolve_artifact_names + def test_o2m_m2o(self): + class Base(_base.ComparableEntity): + pass + class Child(_base.ComparableEntity): + pass + + mapper(Base, base, properties={ + 'children':relation(Child, backref='parent') + }) + mapper(Child, noninh_child) + + sess = create_session() + + c1, c2 = Child(), Child() + b1 = Base(descr='b1', children=[c1, c2]) + sess.add(b1) + + assert c1 in sess.new + assert c2 in sess.new + sess.flush([b1]) + + # c1, c2 get cascaded into the session on o2m. + # not sure if this is how I like this + # to work but that's how it works for now. + assert c1 in sess and c1 not in sess.new + assert c2 in sess and c2 not in sess.new + assert b1 in sess and b1 not in sess.new + + sess = create_session() + c1, c2 = Child(), Child() + b1 = Base(descr='b1', children=[c1, c2]) + sess.add(b1) + sess.flush([c1]) + # m2o, otoh, doesn't cascade up the other way. + assert c1 in sess and c1 not in sess.new + assert c2 in sess and c2 in sess.new + assert b1 in sess and b1 in sess.new + + sess = create_session() + c1, c2 = Child(), Child() + b1 = Base(descr='b1', children=[c1, c2]) + sess.add(b1) + sess.flush([c1, c2]) + # m2o, otoh, doesn't cascade up the other way. + assert c1 in sess and c1 not in sess.new + assert c2 in sess and c2 not in sess.new + assert b1 in sess and b1 in sess.new + + @testing.resolve_artifact_names + def test_circular_sort(self): + """test ticket 1306""" + + class Base(_base.ComparableEntity): + pass + class Parent(Base): + pass + class Child(Base): + pass + + mapper(Base,base) + + mapper(Child, inh_child, + inherits=Base, + properties={'parent': relation( + Parent, + backref='children', + primaryjoin=inh_child.c.parent_id == parent.c.id + )} + ) + + + mapper(Parent,parent, inherits=Base) + + sess = create_session() + p1 = Parent() + + c1, c2, c3 = Child(), Child(), Child() + p1.children = [c1, c2, c3] + sess.add(p1) + + sess.flush([c1]) + assert p1 in sess.new + assert c1 not in sess.new + assert c2 in sess.new + if __name__ == "__main__": testenv.main() |