Django аннотирует Multiple Sum с одним полем Many to Many
Возьмем эти модели:
class Computer(models.Model):
    name = models.CharField(max_length=100, null=False, blank=False)
    cpu = models.PositiveIntegerField(null=False, blank=False)
    ram = models.PositiveBigIntegerField(null=False, blank=False)
    customer = models.ForeignKey(
    Customer,
    related_name='Computers',
    on_delete=models.SET_NULL,
    null=True,
    blank=True
    )
class Disk(models.Model):
    computer = models.ForeignKey(
        Computer,
        related_name='Disks',
        on_delete=models.CASCADE,
        null=False,
        blank=False
    )
    size = models.PositiveBigIntegerField(null=False, blank=False)
class Customer(models.Model):
    name = models.CharField(max_length=255,
                            blank=True, null=True)
    creation_date = models.DateTimeField(auto_now_add=True)
    enabled = models.BooleanField(
        blank=False, null=False, default=False)
Я хочу получить список по клиентам, общее количество CPU, RAM и размер диска (с указанием количества компьютеров для каждого клиента).
примерно так:
{'customer': ACME_Corp., 'cpu__sum': 72, 'ram__sum': 10737418240, 'computer__sum': 10, 'disks__sum': 10000000000000}
Я пробовал нечто подобное:
Computer.values('customer').annotate(Sum('cpu'), Sum('ram'), Sum('Disks__size'),computer__sum=Count('computer_id'))
Проблема заключается в следующем: Если компьютер имеет несколько дисков, я собираюсь добавить его процессор (оперативную память и счетчик) столько раз, сколько у него дисков
например, если у нас есть 2 компьютера с 1 процессором на каждом, но к одному из них подключены два диска, то общее количество процессоров будет не 2, а 3...
Любая помощь будет оценена по достоинству, спасибо!!!
Мы можем работать с подзапросом для определения общего размера диска, и таким образом предотвратить то, что Django делает лишние JOIN:
from django.db.models import OuterRef, Subquery, Sum
Customer.objects.annotate(
    total_cpu=Sum('Computers__cpu'),
    total_ram=Sum('Computers__ram'),
    total_disk=Subquery(
        Disk.objects.filter(computer__customer=OuterRef('pk')).values(
            'computer__customer'
        ).values(
            total_disk=Sum('size')
        ).order_by('computer__customer')[:1]
    )
)
В результате будет получен запрос, который выглядит следующим образом:
SELECT customer.*,
       SUM(computer.cpu) AS total_cpu,
       SUM(computer.ram) AS total_ram,
       (
           SELECT SUM(U0.size) AS total_disk
           FROM disk U0
           INNER JOIN computer U1 ON U0.computer_id = U1.id
           WHERE U1.customer_id = customer.id
           GROUP BY U1.customer_id
           ORDER BY U1.customer_id ASC
           LIMIT 1
       ) AS total_disk
FROM customer
LEFT OUTER JOIN computer ON customer.id = computer.customer_id
GROUP BY customer.id