(Modified/Improved) MultiQuerySet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class MultiQuerySet(object):
    def __init__(self, *args, **kwargs):
        self.querysets = args
        self._count = None
    
    def _clone(self):
        querysets = [qs._clone() for qs in self.querysets]
        return MultiQuerySet(*querysets)
    
    def __repr__(self):
        return repr(list(self.querysets))
                
    def count(self):
        if not self._count:
            self._count = sum([qs.count() for qs in self.querysets])
        return self._count
    
    def __len__(self):
        return self.count()
    
    def __iter__(self):
        for qs in self.querysets:
            for item in qs.all():
                yield item
        
    def __getitem__(self, item):
        indices = (offset, stop, step) = item.indices(self.count())
        items = []
        total_len = stop - offset
        for qs in self.querysets:
            if len(qs) < offset:
                offset -= len(qs)
            else:
                items += list(qs[offset:stop])
                if len(items) >= total_len:
                    return items
                else:
                    offset = 0
                    stop = total_len - len(items)
                    continue

More like this

  1. Chain multiple querysets into one by mattdw 4 years, 7 months ago
  2. Multiple querysets by t_rybik 3 years, 3 months ago
  3. Memory efficient Django Queryset Iterator by WoLpH 3 years, 2 months ago
  4. Manager method for limiting GenericForeignKey queries by zerok 4 years, 9 months ago
  5. Improved Pickled Object Field by taavi223 3 years, 9 months ago

Comments

nosa_manuel (on December 18, 2008):

It would be nice to have these generated transparently:

class QuerySet(object):
    ...
    def __add__(self, other):
        if isinstance(other, QuerySet):
            return MultiQuerySet(self, other)
        elif isinstance(other, MultiQuerySet):
            querysets = other._clone().querysets
            return MultiQuerySet(self, *querysets)
        else:
            raise TypeError

#

esquevin (on October 19, 2012):

I've fixed a bug in the code that occurred with small querysets on pages > 1, the length of former queryset was substracted from the offset, but not from the stop, resulting in variable length pages

def __getitem__(self, item):
    indices = (offset, stop, step) = item.indices(self.count())
    items = []
    total_len = stop - offset
    for qs in self.querysets:
        if len(qs) < offset:
            offset -= len(qs)
        else:
            items += list(qs[offset:offset + total_len - len(items)])
            if len(items) >= total_len:
                return items
            else:
                offset = 0
                continue

#

(Forgotten your password?)