From: Mike Bayer Date: Tue, 5 Sep 2006 16:58:02 +0000 (+0000) Subject: restored "optimistic" behavior of hasparent. its generally disastrous without that... X-Git-Tag: rel_0_2_8~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=20f15720da7b9f6baca01d8bfef359263b671824;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git 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. --- diff --git a/lib/sqlalchemy/attributes.py b/lib/sqlalchemy/attributes.py index b29b03aa8f..af2c908c44 100644 --- a/lib/sqlalchemy/attributes.py +++ b/lib/sqlalchemy/attributes.py @@ -33,8 +33,14 @@ class InstrumentedAttribute(object): 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 @@ -486,12 +492,16 @@ class CommittedState(object): 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__): diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index c7531eb8e7..fe0525b4ac 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -141,8 +141,9 @@ class Mapper(object): #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 diff --git a/test/base/attributes.py b/test/base/attributes.py index abacd0ac96..d1c6978e12 100644 --- a/test/base/attributes.py +++ b/test/base/attributes.py @@ -182,13 +182,14 @@ class AttributesTest(PersistTest): 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()