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 中求和很容易,所以也许不需要打扰数据库。
我有一个 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 中求和很容易,所以也许不需要打扰数据库。