]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
insure that "parent" pointers are set up on objects that were lazily loaded
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 1 Sep 2006 16:25:20 +0000 (16:25 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 1 Sep 2006 16:25:20 +0000 (16:25 +0000)
CHANGES
lib/sqlalchemy/attributes.py
test/base/attributes.py

diff --git a/CHANGES b/CHANGES
index d0cd189409b536eab6f197b451be1efa1b264559..c509caeb6dcecf5b458fac6f732d957f36b6517f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -167,6 +167,7 @@ relationships to an inheriting mapper (which is also self-referential)
 - added 'checkfirst' argument to table.create()/table.drop(), as 
 well as table.exists() [ticket:234]
 - some other ongoing fixes to inheritance [ticket:245]
+- attribute/backref/orphan/history-tracking tweaks as usual...
 
 0.2.5
 - fixed endless loop bug in select_by(), if the traversal hit
index c709afe3dc67cf8207557f9c6c2fbaafc605ed0c..3aada1951a3ab2fb730cda12950bf1e81ff927ab 100644 (file)
@@ -134,7 +134,10 @@ class InstrumentedAttribute(object):
                 if callable_ is not None:
                     if passive:
                         return InstrumentedAttribute.PASSIVE_NORESULT
-                    l = InstrumentedList(self, obj, self._adapt_list(callable_()), init=False)
+                    values = callable_()
+                    l = InstrumentedList(self, obj, self._adapt_list(values), init=False)
+                    if self.trackparent and values is not None:
+                        [self.sethasparent(v, True) for v in values if v is not None]
                     # if a callable was executed, then its part of the "committed state"
                     # if any, so commit the newly loaded data
                     orig = state.get('original', None)
@@ -152,7 +155,10 @@ class InstrumentedAttribute(object):
                 if callable_ is not None:
                     if passive:
                         return InstrumentedAttribute.PASSIVE_NORESULT
-                    obj.__dict__[self.key] = callable_()
+                    value = callable_()
+                    obj.__dict__[self.key] = value
+                    if self.trackparent and value is not None:
+                        self.sethasparent(value, True)
                     # if a callable was executed, then its part of the "committed state"
                     # if any, so commit the newly loaded data
                     orig = state.get('original', None)
index d3ca349316bb97045cfa53dc22d455f36afb2d95..f281e487b5e676134a7c555dc30f465382c710b0 100644 (file)
@@ -131,8 +131,8 @@ class AttributesTest(PersistTest):
         class Post(object):pass
         class Blog(object):pass
         
-        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'))
-        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'))
+        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'), trackparent=True)
+        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'), trackparent=True)
         b = Blog()
         (p1, p2, p3) = (Post(), Post(), Post())
         b.posts.append(p1)
@@ -165,6 +165,32 @@ class AttributesTest(PersistTest):
         j.port = None
         self.assert_(p.jack is None)
 
+    def testlazytrackparent(self):
+        """test that the "hasparent" flag works properly when lazy loaders and backrefs are used"""
+        manager = attributes.AttributeManager()
+
+        class Post(object):pass
+        class Blog(object):pass
+
+        # set up instrumented attributes with backrefs    
+        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'), trackparent=True)
+        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'), trackparent=True)
+
+        # create objects as if they'd been freshly loaded from the database (without history)
+        b = Blog()
+        p1 = Post()
+        Blog.posts.set_callable(b, lambda:[p1])
+        Post.blog.set_callable(p1, lambda:b)
+
+        # 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)
+        
+        
     def testinheritance(self):
         """tests that attributes are polymorphic"""
         class Foo(object):pass