Mike Bayer avatar Mike Bayer committed c84255e

- Fixed bug whereby the "unsaved, pending instance"
FlushError raised for a pending orphan would not take
superclass mappers into account when generating
the list of relations responsible for the error.

Comments (0)

Files changed (3)

       should reduce the probability of "Attribute x was 
       not replaced during compile" warnings. (this generally
       applies to SQLA hackers, like Elixir devs).
+      
+    - Fixed bug whereby the "unsaved, pending instance" 
+      FlushError raised for a pending orphan would not take
+      superclass mappers into account when generating 
+      the list of relations responsible for the error.
 
 - sql
     - func.count() with no arguments renders as COUNT(*),

lib/sqlalchemy/orm/session.py

 """Provides the Session class and related utilities."""
 
 import weakref
-
+from itertools import chain
 import sqlalchemy.exceptions as sa_exc
 from sqlalchemy import util, sql, engine
 from sqlalchemy.sql import util as sql_util, expression
                     ["any parent '%s' instance "
                      "via that classes' '%s' attribute" %
                      (cls.__name__, key)
-                     for (key, cls) in _state_mapper(state).delete_orphans])
+                     for (key, cls) in chain(*(m.delete_orphans for m in _state_mapper(state).iterate_to_root()))])
                 raise exc.FlushError(
                     "Instance %s is an unsaved, pending instance and is an "
                     "orphan (is not attached to %s)" % (

test/orm/inheritance/basic.py

         sess.flush()
         assert sess.query(Sub).one().data == "im the data"
         
-
+class DeleteOrphanTest(ORMTest):
+    def define_tables(self, metadata):
+        global single, parent
+        single = Table('single', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('type', String(50), nullable=False),
+            Column('data', String(50)),
+            Column('parent_id', Integer, ForeignKey('parent.id'), nullable=False),
+            )
+            
+        parent = Table('parent', metadata,
+                Column('id', Integer, primary_key=True),
+                Column('data', String(50))
+            )
+    
+    def test_orphan_message(self):
+        class Base(fixtures.Base):
+            pass
+        
+        class SubClass(Base):
+            pass
+        
+        class Parent(fixtures.Base):
+            pass
+        
+        mapper(Base, single, polymorphic_on=single.c.type, polymorphic_identity='base')
+        mapper(SubClass, inherits=Base, polymorphic_identity='sub')
+        mapper(Parent, parent, properties={
+            'related':relation(Base, cascade="all, delete-orphan")
+        })
+        
+        sess = create_session()
+        s1 = SubClass(data='s1')
+        sess.add(s1)
+        self.assertRaisesMessage(orm_exc.FlushError, 
+            "is not attached to any parent 'Parent' instance via that classes' 'related' attribute", sess.flush)
+        
+    
 if __name__ == "__main__":
     testenv.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.