summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-01-05 23:27:02 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-01-05 23:27:02 +0000
commita78914942ccc26bfad91e367eaaae2dbd29e4439 (patch)
tree7a056b492aca4f87c9fcb91b92221984e964defe
parent8fe38c7e956675eb9b6c859848200d8dcc7a9589 (diff)
downloadsqlalchemy-a78914942ccc26bfad91e367eaaae2dbd29e4439.tar.gz
- synonyms can now be created against props that don't exist yet,
which are later added via add_property(). This commonly includes backrefs. (i.e. you can make synonyms for backrefs without worrying about the order of operations) [ticket:919]
-rw-r--r--CHANGES7
-rw-r--r--lib/sqlalchemy/orm/attributes.py10
-rw-r--r--lib/sqlalchemy/orm/properties.py5
-rw-r--r--test/orm/mapper.py53
4 files changed, 70 insertions, 5 deletions
diff --git a/CHANGES b/CHANGES
index fa9ea42d7..872351a13 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,7 +13,12 @@ CHANGES
- orm
- Fixed cascades on a += assignment to a list-based relation.
-
+
+ - synonyms can now be created against props that don't exist yet,
+ which are later added via add_property(). This commonly includes
+ backrefs. (i.e. you can make synonyms for backrefs without
+ worrying about the order of operations) [ticket:919]
+
- ext
- '+', '*', '+=' and '*=' support for association proxied lists.
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 91bf13034..1639055bd 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -77,10 +77,16 @@ class ProxiedAttribute(InstrumentedAttribute):
def __init__(self, key, user_prop, comparator=None):
self.user_prop = user_prop
- self.comparator = comparator
+ self._comparator = comparator
self.key = key
self.impl = ProxiedAttribute.ProxyImpl(key)
-
+
+ def comparator(self):
+ if callable(self._comparator):
+ self._comparator = self._comparator()
+ return self._comparator
+ comparator = property(comparator)
+
def __get__(self, instance, owner):
if instance is None:
self.user_prop.__get__(instance, owner)
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 8be835986..b2028fdcc 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -155,7 +155,8 @@ class SynonymProperty(MapperProperty):
def do_init(self):
class_ = self.parent.class_
- aliased_property = self.parent._get_property(self.key, resolve_synonyms=True)
+ def comparator():
+ return self.parent._get_property(self.key, resolve_synonyms=True).comparator
self.logger.info("register managed attribute %s on class %s" % (self.key, class_.__name__))
if self.instrument is None:
class SynonymProp(object):
@@ -169,7 +170,7 @@ class SynonymProperty(MapperProperty):
return getattr(obj, self.name)
self.instrument = SynonymProp()
- sessionlib.register_attribute(class_, self.key, uselist=False, proxy_property=self.instrument, useobject=False, comparator=aliased_property.comparator)
+ sessionlib.register_attribute(class_, self.key, uselist=False, proxy_property=self.instrument, useobject=False, comparator=comparator)
def merge(self, session, source, dest, _recursive):
pass
diff --git a/test/orm/mapper.py b/test/orm/mapper.py
index 28885356c..c94c46a01 100644
--- a/test/orm/mapper.py
+++ b/test/orm/mapper.py
@@ -260,6 +260,59 @@ class MapperTest(MapperSuperTest):
assert u.user_name == 'jack'
u.user_name = 'jacko'
assert m._columntoproperty[users.c.user_name] is m.get_property('_user_name')
+
+ def test_synonym_replaces_backref(self):
+ assert_calls = []
+ class Address(object):
+ def _get_user(self):
+ assert_calls.append("get")
+ return self._user
+ def _set_user(self, user):
+ assert_calls.append("set")
+ self._user = user
+ user = property(_get_user, _set_user)
+
+ # synonym is created against nonexistent prop
+ mapper(Address, addresses, properties={
+ 'user':synonym('_user')
+ })
+ compile_mappers()
+
+ # later, backref sets up the prop
+ mapper(User, users, properties={
+ 'addresses':relation(Address, backref='_user')
+ })
+
+ sess = create_session()
+ u1 = sess.query(User).get(7)
+ u2 = sess.query(User).get(8)
+ # comparaison ops need to work
+ a1 = sess.query(Address).filter(Address.user==u1).one()
+ assert a1.address_id == 1
+ a1.user = u2
+ assert a1.user is u2
+ self.assertEquals(assert_calls, ["set", "get"])
+
+ def test_self_ref_syn(self):
+ t = Table('nodes', MetaData(),
+ Column('id', Integer, primary_key=True),
+ Column('parent_id', Integer, ForeignKey('nodes.id')))
+
+ class Node(object):
+ pass
+
+ mapper(Node, t, properties={
+ '_children':relation(Node, backref=backref('_parent', remote_side=t.c.id)),
+ 'children':synonym('_children'),
+ 'parent':synonym('_parent')
+ })
+
+ n1 = Node()
+ n2 = Node()
+ n1.children.append(n2)
+ assert n2.parent is n2._parent is n1
+ assert n1.children[0] is n1._children[0] is n2
+ self.assertEquals(str(Node.parent == n2), ":param_1 = nodes.parent_id")
def test_illegal_non_primary(self):
mapper(User, users)