From: Mike Bayer Date: Sun, 1 Jun 2008 14:08:02 +0000 (+0000) Subject: - The contains() operator when used with many-to-many X-Git-Tag: rel_0_4_7~24 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1c14de62151e1bbae0b9f61092f9fbad34865c94;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - The contains() operator when used with many-to-many will alias() the secondary (association) table so that multiple contains() calls will not conflict with each other [ticket:1058] --- diff --git a/CHANGES b/CHANGES index 581ffce437..1abb13d832 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,12 @@ CHANGES ======= 0.4.7 ===== +- orm + - The contains() operator when used with many-to-many + will alias() the secondary (association) table so + that multiple contains() calls will not conflict + with each other [ticket:1058] + - mysql - Added 'CALL' to the list of SQL keywords which return result rows. diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 33a0ff4326..db7b3a43f9 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -390,7 +390,7 @@ class PropertyLoader(StrategizedProperty): return op(self.comparator, value) def _optimized_compare(self, value, value_is_parent=False): - return self._get_strategy(strategies.LazyLoader).lazy_clause(value, reverse_direction=not value_is_parent) + return self._get_strategy(strategies.LazyLoader).lazy_clause(value, reverse_direction=not value_is_parent, alias_secondary=True) def private(self): return self.cascade.delete_orphan diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 65a8b019b8..1255991a5f 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -270,7 +270,7 @@ class LazyLoader(AbstractRelationLoader): self.is_class_level = True self._register_attribute(self.parent.class_, callable_=self.class_level_loader) - def lazy_clause(self, instance, reverse_direction=False): + def lazy_clause(self, instance, reverse_direction=False, alias_secondary=False): if instance is None: return self._lazy_none_clause(reverse_direction) @@ -286,6 +286,10 @@ class LazyLoader(AbstractRelationLoader): # also its a deferred value; so that when used by Query, the committed value is used # after an autoflush occurs bindparam.value = lambda: mapper._get_committed_attr_by_column(instance, bind_to_col[bindparam.key]) + + if self.secondary and alias_secondary: + criterion = sql_util.ClauseAdapter(self.secondary.alias()).traverse(criterion) + return visitors.traverse(criterion, clone=True, visit_bindparam=visit_bindparam) def _lazy_none_clause(self, reverse_direction=False): diff --git a/test/orm/query.py b/test/orm/query.py index f1afdb90b4..4cfd0ee6b9 100644 --- a/test/orm/query.py +++ b/test/orm/query.py @@ -394,6 +394,10 @@ class FilterTest(QueryTest): assert [Order(id=4), Order(id=5)] == sess.query(Order).filter(~Order.items.contains(item)).all() + item2 = sess.query(Item).get(5) + assert [Order(id=3)] == sess.query(Order).filter(Order.items.contains(item)).filter(Order.items.contains(item2)).all() + + def test_comparison(self): """test scalar comparison to an object instance""" @@ -415,7 +419,7 @@ class FilterTest(QueryTest): # m2m self.assertEquals(sess.query(Item).filter(Item.keywords==None).all(), [Item(id=4), Item(id=5)]) self.assertEquals(sess.query(Item).filter(Item.keywords!=None).all(), [Item(id=1),Item(id=2), Item(id=3)]) - + def test_filter_by(self): sess = create_session() user = sess.query(User).get(8)