Combine QuerySets from two different child models

1,210 views
Skip to first unread message

Luke Seelenbinder

unread,
Nov 19, 2008, 2:04:16 PM11/19/08
to Django users
the models are:

class Word(models.Model):
... some stuff...

class Meta:
abstract=True
class Noun(Word):
.. some stuff ..
class Verb(Word):
... some stuff ...

code:

nouns= Noun.objects.all()
verbs=Verb.objects.all()

words = verbs | nouns

I get an error saying: "Cannot combine queries on two different base
models."
is there anyway to accomplish this, the reason I don't want to do:

nouns= list(Noun.objects.all())
verbs=list(Verb.objects.all())

words = verbs + nouns

is I want to be able to run an order_by on the resulting list or
queryset

thanks,

Luke

Malcolm Tredinnick

unread,
Nov 19, 2008, 7:59:30 PM11/19/08
to django...@googlegroups.com

On Wed, 2008-11-19 at 11:04 -0800, Luke Seelenbinder wrote:
> the models are:
>
> class Word(models.Model):
> ... some stuff...
>
> class Meta:
> abstract=True
> class Noun(Word):
> .. some stuff ..
> class Verb(Word):
> ... some stuff ...
>
> code:
>
> nouns= Noun.objects.all()
> verbs=Verb.objects.all()
>
> words = verbs | nouns
>
> I get an error saying: "Cannot combine queries on two different base
> models."
> is there anyway to accomplish this,

Not at the SQL level. Doing a union of two completely distinct result
sets like this is not really a common enough operation to warrant to
extra complexity in Django's ORM. Realise that, in the general case, the
two querysets could have completely different sets of columns and
everything.

The simplest way to achieve the same effect is order each Queryset
individually. Then you'll have two Python iterators (a queryset is an
iterator) that are sorted appropriately and you just need to merge the
results together into a final sorted iterator. In other words, you're
doing a merge (or tape) sort where you've already sorted the individual
sub-piles.

Regards,
Malcolm

klaasvan...@gmail.com

unread,
Jan 6, 2009, 4:14:56 AM1/6/09
to Django users
since this turns up first I thought I'd post my quick & dirty solution
here for the copy-pasters of the interwebs:

def children(self):
class Union(object):
def __init__(self, cmp_on, sources):
self._sources = sources
self._tops = [None] * len(sources)
self._cmp_on = cmp_on

def next(self):
min_i, min = -1, None
for i, top in enumerate(self._tops):
if not top:
try:
top = self._sources[i].next()
except StopIteration:
top = StopIteration
self._tops[i] = top
if not top == StopIteration:
if min == None:
min = top
min_i = i
else:
if getattr(top, self._cmp_on) < getattr
(min, self._cmp_on):
min = top
min_i = i
if min == None:
raise StopIteration

self._tops[min_i] = None
return min

def __iter__(self):
return self

return Union('title', [
iter(self.page_set.order_by('title').all()),
iter(self.directory_set.order_by('title').all())])

On Nov 20 2008, 1:59 am, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:
Reply all
Reply to author
Forward
0 new messages