Skip to content
This repository has been archived by the owner on Nov 27, 2022. It is now read-only.

Doesn`t work in django 1.8 #17

Open
sergei-iurchenko opened this issue Oct 13, 2015 · 2 comments
Open

Doesn`t work in django 1.8 #17

sergei-iurchenko opened this issue Oct 13, 2015 · 2 comments

Comments

@sergei-iurchenko
Copy link

No description provided.

@henriquebastos
Copy link
Owner

It won't work with 1.8+. Django 1.8 already has support for complex aggregate.

@martsberger
Copy link
Contributor

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants