Commits

Mike Bayer committed a1dcc98

- 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]

Comments (0)

Files changed (4)

 =======
 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.

lib/sqlalchemy/orm/properties.py

             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

lib/sqlalchemy/orm/strategies.py

         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)
             
                 # 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):

test/orm/query.py

 
         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"""
 
         # 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)