42 votes

Annoter une somme de deux champs multipliés

J'ai trois modèles, simplifiés pour l'exemple:

 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)
 

Je souhaite interroger les clients (éventuellement avec un filtre) et annoter le total qu'ils ont dépensé (c'est-à-dire la somme (prix * quantité)

J'ai essayé:
Customer.objects.filter(something).annotate(total_spent=Sum(F('order__lineitem__quantity') * F('order__lineitem__price')))

Il semblerait que Sum () ne puisse pas être utilisé avec des expressions F (). Y a-t-il une autre façon de procéder?

2voto

murgatroid99 Points 5099

Vous pouvez essayer d'utiliser une propriété dans le modèle 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)
 

Ensuite, je pense que vous pouvez annoter le montant total dépensé en utilisant

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

Je ne sais pas comment l'efficacité de cette méthode se rapporte aux autres, mais c'est plus Pythonic / Django-y que l'alternative, qui est d'écrire la requête SQL entière à la main comme dans

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

1voto

Grant Points 1159

Avez-vous envisagé d'utiliser la méthode .extra() ?

Voir l' API Django QuerySet .

1voto

Chad Points 329

Vous devrez probablement déployer votre propre agrégateur personnalisé. Vous pouvez trouver une présentation simple d'un exemple GROUP_CONCAT qui devrait vous aider à démarrer ici: http://harkablog.com/inside-the-django-orm-aggregates.html

0voto

Justin Hamade Points 593

Je viens de tomber dessus et je ne pense pas que cela annote et fonctionnera avec une propriété, voir Django - Pouvez-vous utiliser la propriété comme champ dans une fonction d'agrégation?

Voici ce que j'ai fait.

 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
 

Utilisez ensuite la somme et une liste de compréhension:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X