django - example - Anotar una suma de dos campos multiplicados
django models select (6)
Tengo tres modelos, simplificados para el ejemplo:
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)
Quiero consultar a los clientes (posiblemente con un filtro) y anotar el total que han gastado (es decir, la suma total (precio * cantidad)
Yo he tratado:
Customer.objects.filter(something).annotate(total_spent=Sum(F(''order__lineitem__quantity'') * F(''order__lineitem__price'')))
Parece que Sum () no se puede usar con expresiones F (). Hay otra manera de hacer esto?
¿Has mirado usando el método .extra()
?
Ver la API Django QuerySet .
Me acabo de topar con esto y no creo que anotar y trabajar con una propiedad, ver Django - ¿Se puede usar la propiedad como el campo en una función de agregación?
Aquí esta lo que hice.
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
Luego use la suma y una lista de comprensión:
sum([li.total for li in LineItem.objects.filter(order__customer=some_customer).filter(somefilter)])
Podría intentar usar una propiedad en el modelo LineItem
:
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)
Entonces creo que puedes hacer anotaciones con la cantidad total gastada usando
Customer.objects.filter(something).annotate(total_spent=Sum(''order__lineitem__total''))
No sé cómo se relaciona la eficiencia de este método con otros, pero es más Pythonic / Django-y que la alternativa, que es escribir toda la consulta SQL a mano como en
Customer.objects.raw("SELECT ... from <customer_table_name> where ...")
Probablemente necesites desplegar tu propio agregador personalizado. Puede encontrar un simple recorrido por un ejemplo de GROUP_CONCAT
que debería comenzar aquí: http://harkablog.com/inside-the-django-orm-aggregates.html
Similar a: https://.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: Gracias a @Max, utilizando anotaciones en su lugar agregadas.
Tal vez no necesite esta respuesta ahora, pero si lee la documentación sobre la expresión Suma , necesita declarar el campo output_field
, así:
Customer.objects.filter(something)
.annotate(total_spent=Sum(
F(''order__lineitem__quantity'') *
F(''order__lineitem__price''),
output_field=models.FloatField()
))