Chain multiple querysets into one

 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
class MultiQuerySet(object):
    def __init__(self, *args, **kwargs):
        self.querysets = args
        self._count = None
    
    def count(self):
        if not self._count:
            self._count = sum(len(qs) for qs in self.querysets)
        return self._count
    
    def __len__(self):
        return self.count()
        
    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. Multiple querysets by t_rybik 4 years, 2 months ago
  2. Pagination/Filtering Alphabetically by zain 5 years, 1 month ago
  3. assertQuerysetEqual by coleifer 3 years, 11 months ago
  4. Generating aggregate data across generic relations by coleifer 3 years, 11 months ago
  5. Custom managers with chainable filters by itavor 6 years, 3 months ago

Comments

ericflo (on October 1, 2008):

qs = Event.objects.filter(## title matches ##) | Event.objects.filter(## matches in other fields ##)

#

ericflo (on October 1, 2008):

just kidding (wish there was a comment delete)

#

mattdw (on October 1, 2008):

Heh... thanks, ericflo. Fair suggestion, and qs = qs1 | qs2 will probably cover 90% of cases. This is for the other 10% ;-).

#

dan90 (on October 2, 2008):

Hm. If you don't need full query set functionality, you might want to consider itertools, which has the nice added bonus you don't even need to use query_sets for the same model. e.g.

from itertools import chain
chained_qs = chain(Event.objects.all(), BlogPost.objects.all())
for record in chain_qs:
    print record.slug

(assuming BlogPosts and Events both have a field called 'slug')

that also gets you your original order query set as a special case, and it's very clean.

#

mattdw (on October 2, 2008):

Mine doesn't require querysets to be from the same model either. I looked at itertools.chain, but it doesn't support enough QuerySet functionality to work with django's pagination module, pagination being one of the requirements of this solution. (In particular, there's no way to a. find the length of a chained iterator, or b. slice it arbitrarily. This class supports both of those.)

#

carljm (on October 3, 2008):

Very handy! I've had this exact need before, always worked around it. Nicely done!

#

vbabiy (on March 9, 2009):

Is is exactly what I been looking for. Thanks

#

(Forgotten your password?)