Enhance AppenderQuery to support __len__ (but still not issue needless COUNT)

Issue #818 resolved
Former user created an issue

Currently, using .all() on an AppenderQuery object (from a dynamic_loader) will generate two queries: a SELECT, and an unnecessary SELECT COUNT(). This is because list() is calling __len__.

This could be fixed by removing __len__, or by overriding all() to return list(iter(self)) instead of just list(self).

Comments (7)

  1. jek
    • removed status
    • changed status to open
    • changed milestone to 0.5.0

    ebroder points out that changing def all to return list(iter(self)) hides the len from cpython and sidesteps the issue:

    class Canary(object):
        data = 1, 2, 3
    
        def __init__(self):
            self.len = False
    
        def __len__(self):
            self.len = True
            return len(self.data)
    
        def __iter__(self):
            return iter(self.data)
    
        def self_list(self):
            return list(self)
    
        def iter_list(self):
            return list(iter(self))
    
    # classic behavior of all() + __len__ + __iter__
    c = Canary()
    assert c.self_list() == [2, 3](1,)
    assert c.len is True
    
    # list(iter(...))
    c = Canary()
    assert c.iter_list() == [2, 3](1,)
    assert c.len is False
    
    # direct comsumption by any cpython container still hits len
    c = Canary()
    assert list(c) == [2, 3](1,)
    assert c.len is True
    
    # direct iteration still ok
    c = Canary()
    for _ in c:
        pass
    assert c.len is False
    
  2. Mike Bayer repo owner

    I'm not really sold on this, I think the fact that calling list(obj.collection) versus obj.collection.all() has a performance hit is surprise, but silent, behavior. Whereas calling len() and getting an error is somewhat less surprising (since its consistent with Query, sqlobject, etc. but at least is not silent (and then you just call count()).

  3. Log in to comment