Annotate a sum of two fields multiplied

后端 未结 6 850
闹比i
闹比i 2021-01-30 13:28

I have three models, simplified for the example:

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

class Order(models.Model):
    custo         


        
相关标签:
6条回答
  • 2021-01-30 13:51

    Have you looked at using the .extra() method?

    See the Django QuerySet API.

    0 讨论(0)
  • 2021-01-30 13:52

    You'll probably need to roll out your own custom aggregator. You can find a simple walk through of a GROUP_CONCAT example that should get you started here: http://harkablog.com/inside-the-django-orm-aggregates.html

    0 讨论(0)
  • 2021-01-30 13:53

    Similar to: 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"))
    

    Edited: Thanks to @Max, using annotate instead aggregated.

    0 讨论(0)
  • 2021-01-30 13:54

    Maybe you don't need this answer now, but if you read the documentation about Sum expression , you need to declare the output_field, like this:

    Customer.objects.filter(something)
                    .annotate(total_spent=Sum(
                        F('order__lineitem__quantity') * 
                        F('order__lineitem__price'),   
                        output_field=models.FloatField()
                    ))
    
    0 讨论(0)
  • 2021-01-30 13:56

    I just ran into this and I don't think that annotate and will work with a property, see Django - Can you use property as the field in an aggregation function?

    Here is what I did.

    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
    

    Then use sum and a list comprehension:

    sum([li.total for li in  LineItem.objects.filter(order__customer=some_customer).filter(somefilter)])
    
    0 讨论(0)
  • 2021-01-30 14:11

    You could try using a property in the LineItem model:

    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)
    

    Then I think you can annotate with the total amount spent using

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

    I don't know how the efficiency of this method relates to others, but it is more Pythonic/Django-y than the alternative, which is to write the entire SQL query by hand as in

    Customer.objects.raw("SELECT ... from <customer_table_name> where ...")
    
    0 讨论(0)
提交回复
热议问题