From: Mike Bayer Date: Fri, 16 Sep 2011 00:18:26 +0000 (-0400) Subject: - Fixed a variety of synonym()-related regressions X-Git-Tag: rel_0_7_3~48 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9fae11e38caa3841f20e951f7ff2c5e8f5d09383;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed a variety of synonym()-related regressions from 0.6: - making a synonym against a synonym now works. - synonyms made against a relationship() can be passed to query.join(), options sent to query.options(), passed by name to query.with_parent(). - revised the approach taken earlier to just propagate "property" out from the proxied attr the same way queryable attribute does. --- diff --git a/CHANGES b/CHANGES index 58525eb75a..26b7c14287 100644 --- a/CHANGES +++ b/CHANGES @@ -15,12 +15,13 @@ CHANGES when the Session.is_active is True. [ticket:2241] - - Fixed previously untested function which regressed - in 0.7, can now make a synonym() of a synonym() - again. - - - Another previously unknown feature from 0.6, synonyms - of relationship() can again be passed to join(). + - Fixed a variety of synonym()-related regressions + from 0.6: + - making a synonym against a synonym now works. + - synonyms made against a relationship() can + be passed to query.join(), options sent + to query.options(), passed by name + to query.with_parent(). - Identity map .discard() uses dict.pop(,None) internally instead of "del" to avoid KeyError/warning diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 42fdf7e7f8..2ecd233a78 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -193,6 +193,10 @@ def create_proxied_attribute(descriptor): self.adapter = adapter self.__doc__ = doc + @property + def property(self): + return self.comparator.property + @util.memoized_property def comparator(self): if util.callable(self._comparator): diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 302c374c3c..cb31fadac0 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -67,7 +67,6 @@ class DescriptorProperty(MapperProperty): lambda: self._comparator_factory(mapper), doc=self.doc ) - proxy_attr.property = self proxy_attr.impl = _ProxyImpl(self.key) mapper.class_manager.instrument_attribute(self.key, proxy_attr) @@ -355,17 +354,11 @@ class SynonymProperty(DescriptorProperty): def _proxied_property(self): return getattr(self.parent.class_, self.name).property - @property - def _synonym_resolved_property(self): - return self._proxied_property - def _comparator_factory(self, mapper): prop = self._proxied_property if self.comparator_factory: comp = self.comparator_factory(prop, mapper) - elif isinstance(prop, DescriptorProperty): - comp = prop._comparator_factory(mapper) else: comp = prop.comparator_factory(prop, mapper) return comp diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 20c8280d14..6ef6c3b573 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1582,16 +1582,16 @@ class Query(object): if isinstance(onclause, interfaces.PropComparator): if right_entity is None: - right_entity = onclause.property._synonym_resolved_property.mapper + right_entity = onclause.property.mapper of_type = getattr(onclause, '_of_type', None) if of_type: right_entity = of_type else: - right_entity = onclause.property._synonym_resolved_property.mapper + right_entity = onclause.property.mapper left_entity = onclause.parententity - prop = onclause.property._synonym_resolved_property + prop = onclause.property if not isinstance(onclause, attributes.QueryableAttribute): onclause = prop diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index d7c8b20a2f..4708852ea1 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -459,7 +459,7 @@ def with_parent(instance, prop): """ if isinstance(prop, basestring): mapper = object_mapper(instance) - prop = mapper.get_property(prop) + prop = getattr(mapper.class_, prop).property elif isinstance(prop, attributes.QueryableAttribute): prop = prop.property diff --git a/test/orm/test_joins.py b/test/orm/test_joins.py index af35912f79..ccd42c10bd 100644 --- a/test/orm/test_joins.py +++ b/test/orm/test_joins.py @@ -405,7 +405,8 @@ class JoinTest(QueryTest, AssertsCompiledSQL): ) def test_multi_tuple_form(self): - """test the 'tuple' form of join, now superseded by the two-element join() form. + """test the 'tuple' form of join, now superseded + by the two-element join() form. Not deprecating this style as of yet. diff --git a/test/orm/test_query.py b/test/orm/test_query.py index ec4a328e0b..45390d896c 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1780,7 +1780,8 @@ class SynonymTest(QueryTest): 'name_syn':synonym('name'), 'addresses':relationship(Address), 'orders':relationship(Order, backref='user'), # o2m, m2o - 'orders_syn':synonym('orders') + 'orders_syn':synonym('orders'), + 'orders_syn_2':synonym('orders_syn') }) mapper(Address, addresses) mapper(Order, orders, properties={ @@ -1793,20 +1794,52 @@ class SynonymTest(QueryTest): }) mapper(Keyword, keywords) + def test_options(self): + User, Order = self.classes.User, self.classes.Order + + s = create_session() + def go(): + result = s.query(User).filter_by(name='jack').\ + options(joinedload(User.orders_syn)).all() + eq_(result, [ + User(id=7, name='jack', orders=[ + Order(description=u'order 1'), + Order(description=u'order 3'), + Order(description=u'order 5') + ]) + ]) + self.assert_sql_count(testing.db, go, 1) + + def test_options_syn_of_syn(self): + User, Order = self.classes.User, self.classes.Order + + s = create_session() + def go(): + result = s.query(User).filter_by(name='jack').\ + options(joinedload(User.orders_syn_2)).all() + eq_(result, [ + User(id=7, name='jack', orders=[ + Order(description=u'order 1'), + Order(description=u'order 3'), + Order(description=u'order 5') + ]) + ]) + self.assert_sql_count(testing.db, go, 1) + def test_joins(self): User = self.classes.User for j in ( ['orders', 'items'], ['orders_syn', 'items'], + ['orders_syn_2', 'items'], ['orders', 'items_syn'], ['orders_syn', 'items_syn'], + ['orders_syn_2', 'items_syn'], ): result = create_session().query(User).join(*j).filter_by(id=3).all() assert [User(id=7, name='jack'), User(id=9, name='fred')] == result - @testing.fails_if(lambda: True, "0.7 regression, may not support " - "synonyms for relationship") def test_with_parent(self): Order, User = self.classes.Order, self.classes.User @@ -1814,7 +1847,9 @@ class SynonymTest(QueryTest): ('name', 'orders'), ('name_syn', 'orders'), ('name', 'orders_syn'), + ('name', 'orders_syn_2'), ('name_syn', 'orders_syn'), + ('name_syn', 'orders_syn_2'), ): sess = create_session() q = sess.query(User) @@ -1822,7 +1857,8 @@ class SynonymTest(QueryTest): u1 = q.filter_by(**{nameprop:'jack'}).one() o = sess.query(Order).with_parent(u1, property=orderprop).all() - assert [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o + assert [Order(description="order 1"), + Order(description="order 3"), Order(description="order 5")] == o class ImmediateTest(_fixtures.FixtureTest):