Open sergei-iurchenko opened 9 years ago
When my company updated to Django 1.8, we decided that we liked the aggregate-if api and didn't want to change everywhere we were using it. So we created a file with the following:
from django.db.models import (Count as DjangoCount, Min as DjangoMin, Max as DjangoMax, Sum as DjangoSum,
Avg as DjangoAvg, Case, When, Q, FloatField)
def aggregate_if_to_case_when(*args, **kwargs):
"""
We extend the Django versions of Avg, Count, Min, Max, Sum to use the api that we like from aggregate-if
# TODO: we can probably get rid of this join fix in Django 1.8.2
Join fix: A Q object will default to type AND making it get demoted to an INNER JOIN by
django.db.models.sql.query.update_join_types(). So the line Q() | Q(...) changes
the Q from an AND to an OR causing it to be promoted to a LEFT OUTER JOIN instead.
"""
output_field = kwargs.pop('output_field', None)
if kwargs.get('only'):
only = Q() | Q(kwargs.pop('only'))
if not args or not args[0]:
raise Exception('Must pass a positional expression to use only')
args = (Case(When(only, then=args[0]), output_field=output_field),) + args[1:]
return args, kwargs
class Avg(DjangoAvg):
def __init__(self, *args, **kwargs):
kwargs['output_field'] = FloatField()
args, kwargs = aggregate_if_to_case_when(*args, **kwargs)
super(Avg, self).__init__(*args, **kwargs)
class Count(DjangoCount):
def __init__(self, *args, **kwargs):
args, kwargs = aggregate_if_to_case_when(*args, **kwargs)
super(Count, self).__init__(*args, **kwargs)
class Max(DjangoMax):
def __init__(self, *args, **kwargs):
args, kwargs = aggregate_if_to_case_when(*args, **kwargs)
super(Max, self).__init__(*args, **kwargs)
class Min(DjangoMin):
def __init__(self, *args, **kwargs):
args, kwargs = aggregate_if_to_case_when(*args, **kwargs)
super(Min, self).__init__(*args, **kwargs)
class Sum(DjangoSum):
def __init__(self, *args, **kwargs):
args, kwargs = aggregate_if_to_case_when(*args, **kwargs)
super(Sum, self).__init__(*args, **kwargs)
The Case and When objects introduced in Django 1.8 are very powerful and let you do even more than the aggregate-if package supports, but for the 95% of cases when we don't need the additional power, I find the only=Q API much more clear and easy to use.
It won't work with 1.8+. Django 1.8 already has support for complex aggregate.