]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- The contains() operator when used with many-to-many
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 1 Jun 2008 14:08:02 +0000 (14:08 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 1 Jun 2008 14:08:02 +0000 (14:08 +0000)
will alias() the secondary (association) table so
that multiple contains() calls will not conflict
with each other [ticket:1058]

CHANGES
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/strategies.py
test/orm/query.py

diff --git a/CHANGES b/CHANGES
index 581ffce4376ba375220eed08517d2f63e95aa78a..1abb13d832b6acd58b89537442315d9827010da9 100644 (file)
--- 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.
index 33a0ff4326dad1ce67121c2d1fd5fc545904d8ba..db7b3a43f91304c5a96ea2336d145699e1ca78c4 100644 (file)
@@ -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
index 65a8b019b865a3579061d6fa7b811cfdae73762d..1255991a5fa5e1bf445dd27a4f572437d5800ca3 100644 (file)
@@ -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):
index f1afdb90b40b140660e68b27e8edf700bc3e73dc..4cfd0ee6b950733df292c94f1e53bf0baa65ac8f 100644 (file)
@@ -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)