Skip to main content

Improved API for aggregating using Subquery

Project description

https://travis-ci.org/martsberger/django-sql-utils.svg?branch=master

Django SQL Utils

This package provides utilities for working with Django querysets so that you can generate the SQL that you want, with an API you enjoy.

Subquery Aggregates

The Count aggregation in Django:

Parent.objects.annotate(child_count=Count('child'))

generates SQL like the following:

SELECT parent.*, Count(child.id) as child_count
FROM parent
JOIN child on child.parent_id = parent.id
GROUP BY parent.id

In many cases, this is not as performant as doing the count in a SUBQUERY instead of with a JOIN:

SELECT parent.*,
       (SELECT Count(id)
        FROM child
        WHERE parent_id = parent.id) as child_count
FROM parent

Django allows us to generate this SQL using The Subquery and OuterRef classes:

subquery = Subquery(Child.objects.filter(parent_id=OuterRef('id')).order_by()
                    .values('parent').annotate(count=Count('pk'))
                    .values('count'), output_field=IntegerField())
Parent.objects.annotate(child_count=Coalesce(subquery, 0))

Holy cow! It’s not trivial to figure what everything is doing in the above code and it’s not particularly good for maintenance. SubqueryAggregates allow you to forget all that complexity and generate the subquery count like this:

Parent.objects.annotate(child_count=SubqueryCount('child'))

Phew! Much easier to read and understand. It’s the same API as the original Count just specifying the Subquery version.

Easier API for Exists

If you have a Parent/Child relationship (Child has a ForeignKey to Parent), you can annotate a queryset of Parent objects with a boolean indicating whether or not the parent has children:

from django.db.models import Exists

parents = Parent.objects.annotate(
    has_children=Exists(Child.objects.filter(parent=OuterRef('pk'))
)

That’s a bit more boilerplate than should be necessary, so we provide a simpler API for Exists:

from sql_util.utils import Exists

parents = Parent.objects.annotate(
    has_children=Exists('child')
)

The sql_util version of Exists can also take a queryset as the first parameter and behave just like the Django Exists class, so you are able to use it everywhere without worrying about name confusion.

Installation and Usage

Install from PyPI:

pip install django-sql-utils

Then you can:

from sql_util.utils import SubqueryCount

And use that as shown above.

In addition to SubqueryCount, this package provides SubqueryMin and SubqueryMax. If you want to use other aggregates, you can use the generic SubqueryAggregate class. For example, if the Child model stored an age column and you wanted the average age of each Parent s children:

from django.db.models import Avg, DecimalField

aggregate = SubqueryAggregate('child__age', aggregate=Avg)
Parent.objects.annotate(avg_child_age=aggregate)

Or subclass SubqueryAggregate:

from django.db.models import Avg

class SubqueryAvg(SubqueryAggregate)
    aggregate = Avg
    unordered = True

Parent.objects.annotate(avg_child_age=SubqueryAvg('child__age')

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-sql-utils-0.2.0.tar.gz (8.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_sql_utils-0.2.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file django-sql-utils-0.2.0.tar.gz.

File metadata

  • Download URL: django-sql-utils-0.2.0.tar.gz
  • Upload date:
  • Size: 8.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for django-sql-utils-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0f69a531e1dfde35a21dcdf6c5da20f9a93ec662cfae5637471569f8e03f16ad
MD5 b66736e767e7d24d05ab793e03a1735b
BLAKE2b-256 0d177849ad977ce741046df24917a4cfaa80a9fdc4ad96452b9638854a290869

See more details on using hashes here.

File details

Details for the file django_sql_utils-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: django_sql_utils-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for django_sql_utils-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 63194adee92b661b9347528274c022c30cb614d64b4bb01f585c3ebed5660649
MD5 0e4113bfe46d65669ef5c34b0f02c776
BLAKE2b-256 3ef9400061be37203fb6c87249fe83935f6c33336614881c0d1594792565c04a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page