summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2009-02-07 21:57:30 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2009-02-07 21:57:30 +0000
commit2dba55cb27cf52fb41533ff0cf85096db38b5d9d (patch)
tree297276ed8f29151484f8c9433fa1f8addb59074c
parent1c751e3ddb4768b2ba916da9bc1354887077889a (diff)
downloadsqlalchemy-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--CHANGES4
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py2
-rw-r--r--test/orm/cascade.py111
3 files changed, 116 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index 4ffa3a86a..ef8d2b3d4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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()