django-compoundqueryset

Allows for creation of compound querysets in Django.


Keywords
django, compound, queryset
License
Other
Install
pip install django-compoundqueryset==0.1.3

Documentation

Django-compoundqueryset

PyPi

A little Django app allows for compound querysets between different tables. Especially useful for pagination of said results.

Django versions supported: 1.8, 1.9, 1.10

Python versions supported: 2.7, 3.3, 3.4, 3.5

Installation

You can obtain the source code for django-compoundqueryset from here:

https://github.com/brianwawok/django-compoundqueryset

Using pip:

pip install django-compoundqueryset

Motivation

Sometimes you might want to combine two different tables with similar fields and show them to users. For example:

from django.db import models



class FirstModel(models.Model):
    age = models.IntegerField()
    name = models.TextField(max_length=100)
    color = models.TextField(max_length=100)


class SecondModel(models.Model):
    age = models.IntegerField()
    name = models.TextField(max_length=100)
    size = models.IntegerField()

And you want to paginate across them

from django.core.paginator import Paginator
from djcompoundqueryset import CompoundQueryset


qs_1 = FirstModel.objects.order_by('age').all()
qs_2 = SecondModel.objects.order_by('age').all()
combined_queryset = qs_1 | qs_2

p = Paginator(combined_queryset, 10)

You will get the dreaded error

Merging 'QuerySet' classes must involve the same values in each case.

Now with django-compoundqueryset, you can do:

from django.core.paginator import Paginator
from djcompoundqueryset import CompoundQueryset


qs_1 = FirstModel.objects.order_by('age').all()
qs_2 = SecondModel.objects.order_by('age').all()
combined_queryset = CompoundQueryset(qs_1, qs_2, max_items=100)

p = Paginator(combined_queryset, 10)

You can now iterate over these models in a view, showing the age. Max_items says you will never use more than that many items, so limit the count a queries. Useful if you have capped pagination.

Performance

The goal is to beat the naive implementation (load all records into memory and deal with there), but we may not beat a RawSQL union all monster. If performance is really important you may need to do that.

If used in a Django Paginator, we will normally do 1 count(*) query per queryset, and then only load the required records.