【问题标题】:Django annotate Multiple Sum with one Many to Many fieldDjango用一个多对多字段注释多个总和
【发布时间】:2021-10-22 13:00:50
【问题描述】:

让我们来看看这个模型:

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'))

问题是: 如果一台计算机有多个磁盘,我将添加他的 cpu(RAM 和计数)与他的磁盘数量一样多

例如,如果我们有 2 台计算机,每台计算机有 1 个 cpu,但其中一台连接了两个磁盘,则 cpu 总数不会是 2,而是 3...

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 每台电脑都至少有一个磁盘吗?
  • 是的,每台电脑至少有一个磁盘!

标签: django postgresql django-models


【解决方案1】:

我们可以使用子查询来确定总磁盘大小,从而防止 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

【讨论】:

  • 非常感谢!它可以工作。但是,如果计算机没有客户怎么办?也许我可以将客户设置为虚拟客户而不是“无”...
  • @Fumble:如果计算机没有客户,则它不是聚合的一部分。如果您希望将这些内容包含在结果中,您确实可以使用虚拟用户。
猜你喜欢
  • 1970-01-01
  • 2011-02-05
  • 2011-11-10
  • 2020-07-09
  • 1970-01-01
  • 2021-11-03
  • 2023-04-09
  • 2021-10-09
  • 2019-01-13
相关资源
最近更新 更多