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