2009-04-30 14 views
32

Mam trzy modele uproszczone dla przykładu:Opisywanie sumą dwóch pól pomnożone

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) 

Chcę zapytać klientów (ewentualnie z filtrem) i opisywanie łącznie wydali (czyli suma ciągu (cena * ilość)

próbowałem.?
Customer.objects.filter(something).annotate(total_spent=Sum(F('order__lineitem__quantity') * F('order__lineitem__price')))

Wydaje się, że Sum() nie może być używany z F() wyrażeń Czy jest jakiś inny sposób to zrobić

+0

Czy kiedykolwiek uzyskać to do pracy? – dotty

+0

Szukam również rozwiązania – Don

+0

Możliwy duplikat [Django Aggregation: Podsumowanie mnożenia dwóch pól] (http://stackoverflow.com/questions/12165636/django-aggregation-summation-of-multiplication-of-two -fields) – Louis

Odpowiedz

1

Czy przyjrzałeś się metodzie .extra()?

Zobacz Django QuerySet API.

+3

Mam. Działa, ale staram się tego uniknąć z dwóch powodów: Po pierwsze, używa podzapytania dla poszczególnych wierszy zamiast łączenia, które mogą być skalowalne dla niektórych baz danych.Po drugie, nie działa z filtrem() na dodatkowym polu, więc nie można go łączyć proceduralnie z innymi obiektami Q – Sam

1

Można spróbować użyć nieruchomości w LineItem modelu

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) 

to myślę, że można opisywać z całkowitej kwoty wydanej za pomocą

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

nie wiem jak sprawność ta metoda odnosi się do innych, ale jest bardziej Pythonic/Django-y niż alternatywa, która polega na pisaniu całego zapytania SQL ręcznie jak w

Customer.objects.raw("SELECT ... from <customer_table_name> where ...") 
+0

Próbowałem tego i to nie działa, otrzymuję słowo kluczowe "Nie mogę rozwiązać słowa kluczowego" błąd pola. :-( –

+0

OK, od komentarza do drugiej odpowiedzi wygląda na to, że nie można tego zrobić za pomocą adnotacji na właściwości. – murgatroid99

+0

ORM Django generuje zapytania, które wykonują obliczenia za pomocą SQL, a zatem nie można uwzględnić właściwości zdefiniowanych w Pythonie w metodach jako część zapytania. Możesz zastosować zapytanie o zrozumienie listy, ale to nie jest to samo, co Justin Hamade zauważył: – acjay

1

po prostu wpadł na to i nie sądzę, że opisywanie i będzie działać z usługą, zobacz Django - Can you use property as the field in an aggregation function?

Oto co zrobiłem.

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 

Następnie użyj sumę i wyrażeń listowych:

sum([li.total for li in LineItem.objects.filter(order__customer=some_customer).filter(somefilter)]) 
+0

, które nie działa, ponieważ właściwości są obliczane po zapytaniu –

+0

Ta opcja działa, ale robisz sumę w python nie sql, co oznacza, że ​​będzie dużo wolniej, ponieważ staje się duża –

+0

Czy ta metoda aktualizuje się podczas dodawania kolejnych zamówień? – agconti

0

podobne do: 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")) 

edycja: Dzięki @Max, używając adnotacji zamiast zagregowane.

+1

jesteś we właściwy sposób: , ale zamiast agregować użyj adnotacji i usuń [ "total"] W wyniku: total = Task.objects.filter (your-fi lter-here) .annotate (total = Sum ('progress', field = "progress * estim_days")). Remake przykład, aby być pozytywnym – Max

6

Może nie trzeba tę odpowiedź teraz, ale jeśli przeczytać dokumentację o Sum expression, trzeba zadeklarować output_field coś takiego:

Customer.objects.filter(something) 
       .annotate(total_spent=Sum(
        F('order__lineitem__quantity') * 
        F('order__lineitem__price'), 
        output_field=models.FloatField() 
       )) 
Powiązane problemy