Agregações condicionais com Django Aggregate If

2

O django-aggregate-if implementa agregações condicionais no Django.

Agregações condicionais como SumIf e CountIf são famosos nas planilhas. Na sua aplicação web, elas podem ajudar a reduzir a quantidade de queries para obter informações como estatísticas, por exemplo.

Imagine que você tem um modelo Offer, como este:

class Offer(models.Model):
    sponsor = models.ForeignKey(User)
    price = models.DecimalField(max_digits=9, decimal_places=2)
    status = models.CharField(max_length=30)
    expire_at = models.DateField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    OPEN = "OPEN"
    REVOKED = "REVOKED"
    PAID = "PAID"

Digamos que você queira saber:

  1. Quantas ofertas existem no total;
  2. Quantas ofertas estão OPEN, REVOKED ou PAID;
  3. Quanto dinheiro foi oferecido no total;
  4. Quanto dinheiro está em ofertas OPEN, REVOKED e PAID;

Para obter estas informações você poderia adicionar SQL direto com o método extra do QuerySet, ou executar as queries:

from django.db.models import Count, Sum

Offer.objects.count()
Offer.objects.filter(status=Offer.OPEN).aggregate(Count('pk'))
Offer.objects.filter(status=Offer.REVOKED).aggregate(Count('pk'))
Offer.objects.filter(status=Offer.PAID).aggregate(Count('pk'))
Offer.objects.aggregate(Sum('price'))
Offer.objects.filter(status=Offer.OPEN).aggregate(Sum('price'))
Offer.objects.filter(status=Offer.REVOKED).aggregate(Sum('price'))
Offer.objects.filter(status=Offer.PAID).aggregate(Sum('price'))

Neste caso, 8 consultas ao banco foram necessárias para obter as informações desejadas.

Com as agregações condicionais do django-aggregate-if você pode obter tudo com apenas 1 query:

from django.db.models import Q
from aggregate_if import Count, Sum

Offer.objects.aggregate(
    Count('pk'),
    Count('pk', only=Q(status=Offer.OPEN)),
    Count('pk', only=Q(status=Offer.REVOKED)),
    Count('pk', only=Q(status=Offer.PAID)),
    Sum('price'),
    Sum('price', only=Q(status=Offer.OPEN)),
    Sum('price'), only=Q(status=Offer.REVOKED)),
    Sum('price'), Q(status=Offer.PAID)),
)

Inspiração

Existe um ticket 11305 que (com sorte) adicionará essa capacidade ao ORM do Django 1.6.

No entanto, refatorando a página de estatísticas do FreedomSponsors.org, decidi implementar o django-aggregate-if para trazer esta funcionalidade para o Django 1.4.

A biblioteca utiliza a mesma API e testes propostos no ticket 11305, então quando a funcionalidade estiver disponível no Django, você poderá facilmente substituir o django-aggregate-if.

O código está no Github e toda contribuição é sempre bem-vinda. 😉

[]’s, HB!

você pode gostar também
2 comentários
  1. Marcius Oliveira Diz

    Maneiro mesmo Henrique, parabéns

  2. Marcius Oliveira Diz

    Maneiro mesmo Henrique, parabéns

Deixe uma resposta

Seu endereço de email não será publicado.