summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-09-01 16:25:20 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-09-01 16:25:20 +0000
commit005603e2fb198c3f3328fa555e82a334f0f89d52 (patch)
treebfec82270157f25d8335a44c21179a179614a257
parent2c1006534e2bb9e3e468e6ce47181f7a1f8a8de8 (diff)
downloadsqlalchemy-005603e2fb198c3f3328fa555e82a334f0f89d52.tar.gz
insure that "parent" pointers are set up on objects that were lazily loaded
-rw-r--r--CHANGES1
-rw-r--r--lib/sqlalchemy/attributes.py10
-rw-r--r--test/base/attributes.py30
3 files changed, 37 insertions, 4 deletions
diff --git a/CHANGES b/CHANGES
index d0cd18940..c509caeb6 100644
--- 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
diff --git a/lib/sqlalchemy/attributes.py b/lib/sqlalchemy/attributes.py
index c709afe3d..3aada1951 100644
--- a/lib/sqlalchemy/attributes.py
+++ b/lib/sqlalchemy/attributes.py
@@ -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)
diff --git a/test/base/attributes.py b/test/base/attributes.py
index d3ca34931..f281e487b 100644
--- a/test/base/attributes.py
+++ b/test/base/attributes.py
@@ -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