Django 管理注解 - 相关模型的求和字段

Django admin annotation - Sum fields of related models

我有一个 django-admin dashboard/listing,它试图使用我认为无法正常工作的带注释的查询集来跟踪和总结 "jobs"。作为 table 的一部分,我显示了相关对象数据量的计算字段(由带注释的查询集提供)。

实际上它是一个作业列表,用于跟踪针对计算机设备及其各自存储设备的工作,因此一个 作业 可以有多个 设备可以有多个 Storage 设备。设备和存储都可以包含数据量(就像 phone 可以有内部存储器和可移动存储卡一样。)对于某些作业,设备可能是裸硬盘驱动器或带有 5 个硬盘驱动器的 PC 塔,所以我我正在尝试适应这些情况...

models.py:

class Job(models.Model):
    ...
    job_number = models.CharField(max_length=50)
    job_name = models.CharField(max_length=100)
    ...

class Device(models.Model):
    ...
    device_number = models.CharField(max_length=50)
    job = models.ForeignKey(Job, null=True, on_delete=models.CASCADE)
    capacity = models.FloatField(null=True)
    ...

class Storage(models.Model):
    ...
    storage_number = models.CharField(max_length=50)
    device = models.ForeignKey(Device, null=True, on_delete=models.CASCADE)
    capacity = models.FloatField(null=True)
    ...

对于 Job 的管理模型,我尝试了以下方法,似乎 有效,但是当我过滤时(基于对作业名称的文本搜索示例)数据量的总数激增

admin.py

class JobAdmin(admin.ModelAdmin)

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(
            _device_count=Count("device", distinct=True),
            _device_volume=Sum("device__capacity", distinct=True)+Sum("device__storage__capacity", distinct=True)
           )
        return queryset

    def data_label(self,obj):
        return obj._device_volume

我注意到,如果我 search/filter 针对一个工作名称并获得多次匹配,它似乎将数据总和乘以结果数,但如果我在其他字段上进行过滤(使用 list_filter 字段)它不会发生。

谁能看出我错在哪里?我很感激任何建议。

汉族

尽量不要将多个 聚合 annotate() 结合使用,因为 this will yield the wrong results (因为使用连接而不是 子查询)

那么怎么做呢?使用 subqueries,类似的东西应该可以完成工作:

from django.db.models import OuterRef, Subquery, Sum, Count


def get_queryset(self, request):
    queryset = super().get_queryset(request)
    queryset = queryset.annotate(
        _device_count=Subquery(
            Device.objects.filter(job=OuterRef("pk"))
            .values("job")
            .annotate(cnt=Count("id"))
            .values("cnt")
        ),
        _device_capacity=Subquery(
            Device.objects.filter(job=OuterRef("pk"))
            .values("job")
            .annotate(vol=Sum("capacity"))
            .values("vol")
        ),
        _storage_capacity=Subquery(
            Storage.objects.filter(device__job=OuterRef("pk"))
            .values("device__job")
            .annotate(vol=Sum("capacity"))
            .values("vol")
        ),
    )
    return queryset

您可以尝试再做一个注释以获得 _device_capacity_storage_capacity 的总和,但我想在 python 中求和很容易,所以也许不需要打扰数据库。