【问题标题】:django admin list_display with count annotation带有计数注释的 django admin list_display
【发布时间】:2019-05-13 18:35:37
【问题描述】:

我想在 Django Admin 的 list_display 中添加“注释计数”字段。

models.py

class Log(models.Model):
    ...
    ip_address = models.GenericIPAddressField(
        verbose_name=_('IP address'),
        db_index=True,
    )
    ...

admin.py

class LogAdmin(admin.ModelAdmin):
    list_display = (..., 'ip_address', 'ip_address_count', ...)

    def ip_address_count(self, instance):
        return models.Log.objects \
            .filter(ip_address=instance.ip_address) \
            .count()

    ip_address_count.short_description = _('IP Address Count')

它适用于“大量”SQL 查询。

我想像这样改进我的admin.py

class Log(models.Model):
    ...

    def get_queryset(self, request):
        qs = super(LogAdmin, self).get_queryset(request)
        qs = qs.values('ip_address') \
               .annotate(ip_address_count=Count('ip_address'))
        return qs

    def ip_address_count(self, instance):
        return instance.ip_address_count

但是,我遇到了错误消息:

'dict' object has no attribute '_meta'

看来我找到了错误发生的原因:

Django Admin, can't groupe by: Exception Value: 'dict' object has no attribute '_meta'

但是,它并不能帮助我解决问题。

感谢您的回答。

编辑:如果我不附加.values('ip_address),它会出错。

它不是group by ip_address。它group by 所有字段group by field1, field2, ip_address, ... 因此,它导致所有情况为“1”。

【问题讨论】:

  • 您仍然收到错误'dict' object has no attribute '_meta'
  • @SachinKukreja 是的,我是。很遗憾。 :-(

标签: django django-admin django-orm


【解决方案1】:

那是因为当您将values 应用于queryset 时,它会返回一个字典列表,而管理员期望的是objects 的查询集。

我以前也遇到过这种情况,所以我做了这样的事情:

def changelist_view(self, request, extra_context=None):
    response = super().changelist_view(request, extra_context)
    try:
        # this is the final queryset to be rendered on the page after pagination.
        _cl = response.context_data['cl']
        qs = _cl.result_list._clone()
        # since in my case mysql 5.5 does'nt support subquery with LIMIT
        # fetch all the ips on that page
        ips = list(set([obj.ip_address for obj in qs]))
        result_qs = models.Log.objects.values('ip_address') \
                .filter(ip_address__in=ips) \
                .annotate(ip_address_count=Count('ip_address'))
        result = {_r['ip_address']: _r['ip_address_count'] for _r in result_qs}
        setattr(self, '_ip_addr_count', result)
    except:
        pass
    return response

def ip_address_count(self, instance):
    # fetch the count from the dict we set above
    return self._ip_addr_count.get(instance.ip_address)

因此您看到我所做的是,从最终查询集中获取 ips,然后使用它来查询计数。通过这样做,您将在每页仅查询一次数据库。

希望你能得到基本的基本思想。请根据你的需要来适应它。

【讨论】:

【解决方案2】:

这是您可以在本地执行的操作。 Django 知道如何处理这个问题(请参阅有关 admin and list_display 的文档)。

class LogAdmin(admin.ModelAdmin):
    list_display = (..., "field1", "field2"... "quantity_of_things",)

    def get_queryset(self, request):
        return (super(LogAdmin, self).get_queryset(request).annotate(quantity_of_things=Count("xxxxxx")))

    def quantity_of_things(self, obj):
        return obj.quantity_of_things
    quantity_of_things.short_description = "Number of xxxxxx"

你可以用你需要的任何东西来注释查询集(例如Count)。如您所见,您只需使用相同的名称 3 次(在我的示例中为quantity_of_things

  1. 在 list_display 中(Django 理解这是一个可调用对象),
  2. 在注释中,
  3. 在要调用的函数中。
  4. quantity_of_things.short_description 用于列标题

【讨论】:

    猜你喜欢
    • 2019-07-28
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 2022-01-19
    • 2021-09-03
    • 2017-04-09
    • 2023-02-14
    • 2021-02-02
    相关资源
    最近更新 更多