Commits

Mike Bayer committed 49490df

restored "optimistic" behavior of hasparent. its generally disastrous without that flag as its impossible to load all lazy loaders, deal with attributes that "noload", etc. just to check for orphan status.

  • Participants
  • Parent commits b2b8128

Comments (0)

Files changed (3)

File lib/sqlalchemy/attributes.py

 
     def hasparent(self, item, optimistic=False):
         """return the boolean value of a "hasparent" flag attached to the given item.
+        
+        the 'optimistic' flag determines what the default return value should be if 
+        no "hasparent" flag can be located.  as this function is used to determine if 
+        an instance is an "orphan", instances that were loaded from storage should be assumed
+        to not be orphans, until a True/False value for this flag is set.  an instance attribute
+        that is loaded by a callable function will also not have a "hasparent" flag.
         """
-        return item._state.get(('hasparent', id(self)), False)
+        return item._state.get(('hasparent', id(self)), optimistic)
         
     def sethasparent(self, item, value):
         """sets a boolean flag on the given item corresponding to whether or not it is
         if value is not False:
             if attr.uselist:
                 self.data[attr.key] = [x for x in value]
-                if attr.trackparent:
-                    [attr.sethasparent(x, True) for x in self.data[attr.key] if x is not None]
+                # not tracking parent on lazy-loaded instances at the moment.
+                # its not needed since they will be "optimistically" tested
+                #if attr.trackparent:
+                #    [attr.sethasparent(x, True) for x in self.data[attr.key] if x is not None]
             else:
                 self.data[attr.key] = value
-                if attr.trackparent and value is not None:
-                    attr.sethasparent(value, True)
+                # not tracking parent on lazy-loaded instances at the moment.
+                # its not needed since they will be "optimistically" tested
+                #if attr.trackparent and value is not None:
+                #    attr.sethasparent(value, True)
 
     def rollback(self, manager, obj):
         for attr in manager.managed_attributes(obj.__class__):

File lib/sqlalchemy/orm/mapper.py

         #self.compile()
     
     def _is_orphan(self, obj):
+        optimistic = has_identity(obj)
         for (key,klass) in self.delete_orphans:
-            if not getattr(klass, key).hasparent(obj):
+            if not getattr(klass, key).hasparent(obj, optimistic=optimistic):
                 if not has_identity(obj):
                     raise exceptions.FlushError("instance %s is an unsaved, pending instance and is an orphan" % obj)
                 return True

File test/base/attributes.py

         Blog.posts.set_callable(b, lambda:[p1])
         Post.blog.set_callable(p1, lambda:b)
         manager.commit(p1, b)
+
+        # no orphans (called before the lazy loaders fire off)
+        assert getattr(Blog, 'posts').hasparent(p1, optimistic=True)
+        assert getattr(Post, 'blog').hasparent(b, optimistic=True)
+
         # assert connections
         assert p1.blog is b
         assert p1 in b.posts
-
-        # no orphans
-        assert getattr(Blog, 'posts').hasparent(p1)
-        assert getattr(Post, 'blog').hasparent(b)
         
         # manual connections
         b2 = Blog()