Commits

Mike Bayer committed c0417cd

- removed __len__ from "dynamic" collection as it would require issuing
a SQL "count()" operation, thus forcing all list evaluations to issue
redundant SQL [ticket:818]

Comments (0)

Files changed (4)

   collection doesn't contain the item, supports noload collections
   [ticket:813]
 
+- removed __len__ from "dynamic" collection as it would require issuing
+  a SQL "count()" operation, thus forcing all list evaluations to issue
+  redundant SQL [ticket:818]
+  
 - inline optimizations added to locate_dirty() which can greatly speed up
   repeated calls to flush(), as occurs with autoflush=True [ticket:816]
   

lib/sqlalchemy/orm/dynamic.py

         else:
             return sess
             
-    def __len__(self):
-        sess = self.__session()
-        if sess is None:
-            return len(self.attr.get_history(self.state)._added_items)
-        else:
-            return self._clone(sess).count()
-        
     def __iter__(self):
         sess = self.__session()
         if sess is None:

test/orm/dynamic.py

         assert [User(id=7, addresses=[Address(id=1, email_address='jack@bean.com')])] == q.filter(User.id==7).all()
         assert fixtures.user_address_result == q.all()
 
+    def test_no_count(self):
+        mapper(User, users, properties={
+            'addresses':dynamic_loader(mapper(Address, addresses))
+        })
+        sess = create_session()
+        q = sess.query(User)
+
+        # dynamic collection cannot implement __len__() (at least one that returns a live database
+        # result), else additional count() queries are issued when evaluating in a list context
+        def go():
+            assert [User(id=7, addresses=[Address(id=1, email_address='jack@bean.com')])] == q.filter(User.id==7).all()
+        self.assert_sql_count(testbase.db, go, 2)
+        
 class FlushTest(FixtureTest):
     def test_basic(self):
         class Fixture(Base):

test/testlib/fixtures.py

                     except AttributeError:
                         #print "Other class does not have attribute named '%s'" % attr
                         return False
-                    if len(value) != len(getattr(other, attr)):
+                    if not hasattr(value, '__len__'):
+                        value = list(iter(value))
+                        otherattr = list(iter(otherattr))
+                    if len(value) != len(otherattr):
                         #print "Length of collection '%s' does not match that of other" % attr
                         return False
-                    for (us, them) in zip(value, getattr(other, attr)):
+                    for (us, them) in zip(value, otherattr):
                         if us != them:
                             #print "1. Attribute named '%s' does not match other" % attr
                             return False