通过外键和Django Admin中的其他字段进行汇总

时间:2019-10-02 12:03:56

标签: python django django-admin

我正在使用Django,但在管理网站中无法正确显示某些内容时遇到了问题。这些是模型

class IndexSetSize(models.Model):
    """ A time series of sizes for each index set """
    index_set = models.ForeignKey(IndexSet, on_delete=models.CASCADE)
    byte_size = models.BigIntegerField()
    timestamp = models.DateTimeField()

class IndexSet(models.Model):
    title = models.CharField(max_length=4096)
    # ...  some other stuff that isn't really important
    def __str__(self):
        return f"{self.title}"

它正在显示我需要的所有适当数据,但是,我想显示IndexSetSize的总和,按index_set键分组,还按时间戳分组(给定时间戳可能多次出现IndexSet,所以我想把所有的byte_sizes加起来。目前仅显示每条记录。另外,我希望total_size字段可排序

当前的管理员模型如下:

class IndexSetSizeAdmin(admin.ModelAdmin):
    """ View-only admin for index set sizes """
    fields = ["index_set", "total_size", "byte_size", "timestamp"]
    list_display = ["index_set", "total_size", "timestamp"]
    search_fields = ["index_set"]
    list_filter = ["index_set__title"]

    def total_size(self, obj):
        """ Returns human readable size """
        if obj.total_size:
            return humanize.naturalsize(obj.total_size)
        return "-"
    total_size.admin_order_field = 'total_size'

    def get_queryset(self, request):
        queryset = super().get_queryset(request).select_related()
        queryset = queryset.annotate(
            total_size=Sum('byte_size', filter=Q(index_set__in_graylog=True)))
        return queryset

在Django中进行分组的正确方法似乎是使用.values(),尽管如果我在get_queryset中使用它,则会抛出一个错误,提示Cannot call select_related() after .values() or .values_list()。我无法在文档中找到是否有一种“正确”的方法来对值/注释/聚合进行处理,该方法可以与get_queryset一起正常使用。这是我尝试做的一个非常简单的按查询汇总/分组,但是我不确定用什么“ Django方式”来完成它。

谢谢

1 个答案:

答案 0 :(得分:0)

我认为您无法返回完整的查询集并按index_set中的get_queryset进行分组,因为您无法选择所有列,而是按sql中的单个列进行分组

SELECT *, SUM(index_size) FROM indexsetsize GROUP BY index_set   // doesn't work

您可以在total_size方法中执行额外的查询以获取汇总值。但是,这将对返回的每一行执行查询,并降低页面加载速度。

    def total_size(self, obj):
        """ Returns human readable size """
        return humanize.naturalsize(sum(IndexSetSize.objects.filter(
                                        index_set=obj.index_set).values_list(
                                       'byte_size', flat=True)))
    total_size.admin_order_field = 'total_size'

最好在IndexSetAdmin中执行此注释,因为index_set已经通过反向外键分组了。这意味着您可以在get_queryset中执行注释。我还将在related_name的外键上设置IndexSetSize,这样您就可以使用该名称从IndexSetSize访问实际的IndexSet对象。

class IndexSetSize(models.Model):
    index_set = models.ForeignKey(IndexSet, on_delete=models.CASCADE, related_name='index_set_sizes')
    ...

class IndexSetAdmin(admin.ModelAdmin):
    ...

    def total_size(self, obj):
        """ Returns human readable size """
        if obj.total_size:
            return humanize.naturalsize(obj.total_size)
        return "-"

    def get_queryset(self, request):
        queryset = super().get_queryset(request).prefetch_related('index_set_sizes').annotate(
            total_size=Sum('index_set_sizes__byte_size')).order_by('total_size')
        return queryset