Anotar uma soma de dois campos multiplicado

votos
32

Eu tenho três modelos, simplificados para o exemplo:

class Customer(models.Model):
    email = models.CharField(max_length=128)

class Order(models.Model):
    customer = models.ForeignKey(Customer)
    order_status = models.CharField(blank=True, max_length=256)

class Lineitem(models.Model):
    order = models.ForeignKey(Order)
    quantity = models.IntegerField(blank=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)

Quero consultar os clientes (possivelmente com um filtro) e anotar o total que passaram (ou seja, a soma sobre (preço * quantidade)

Eu tentei:
Customer.objects.filter(something).annotate(total_spent=Sum(F('order__lineitem__quantity') * F('order__lineitem__price')))

Parece que Sum () não pode ser usado com F (expressões). Há outra maneira de fazer isso?

Publicado 30/04/2009 em 18:46
fonte usuário
Em outras línguas...                            


6 respostas

votos
6

Talvez você não precisa esta resposta agora, mas se você ler a documentação sobre expressão Sum , você precisa declarar o output_field, como este:

Customer.objects.filter(something)
                .annotate(total_spent=Sum(
                    F('order__lineitem__quantity') * 
                    F('order__lineitem__price'),   
                    output_field=models.FloatField()
                ))
Respondeu 15/02/2016 em 15:53
fonte usuário

votos
1

Você provavelmente vai precisar para lançar o seu próprio agregador de costume. Você pode encontrar uma simples caminhada através de um GROUP_CONCATexemplo que deve começar aqui: http://harkablog.com/inside-the-django-orm-aggregates.html

Respondeu 16/10/2013 em 22:56
fonte usuário

votos
1

Eu corri para isso e eu não acho que anotar e vai trabalhar com uma propriedade, consulte Django - você pode usar a propriedade como o campo em uma função de agregação?

Aqui está o que eu fiz.

class Customer(models.Model):
    email = models.CharField(max_length=128)

class Order(models.Model):
    customer = models.ForeignKey(Customer)
    order_status = models.CharField(blank=True, max_length=256)

class Lineitem(models.Model):
    order = models.ForeignKey(Order)
    quantity = models.IntegerField(blank=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    @property
    def total(self):
        return self.quantity * self.price

Em seguida, use soma e uma compreensão da lista:

sum([li.total for li in  LineItem.objects.filter(order__customer=some_customer).filter(somefilter)])
Respondeu 26/11/2011 em 21:20
fonte usuário

votos
1

Você pode tentar usar uma propriedade no LineItemmodelo:

class Lineitem(models.Model):
    order = models.ForeignKey(Order)
    quantity = models.IntegerField(blank=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    def _get_total(self):
        return quantity * price
    total = property(_get_total)

Então eu acho que você pode fazer anotações com o valor total gasto com

Customer.objects.filter(something).annotate(total_spent=Sum('order__lineitem__total'))

Eu não sei como a eficiência deste método relaciona com os outros, mas é mais Pythonic / Django-y do que a alternativa, que é escrever toda a consulta SQL à mão como em

Customer.objects.raw("SELECT ... from <customer_table_name> where ...")
Respondeu 24/08/2011 em 16:07
fonte usuário

votos
1

Você já olhou para usando o .extra()método?

Veja a API Django QuerySet .

Respondeu 30/04/2009 em 18:54
fonte usuário

votos
0

Semelhante a: https://stackoverflow.com/a/19888120/1344647

from django.db.models import Sum

q = Task.objects.filter(your-filter-here).annotate(total=Sum('progress', field="progress*estimated_days"))

Editado: Graças a @Max, utilizando-se anotar em vez agregadas.

Respondeu 18/06/2015 em 02:51
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more