4

如何在 django admin 的 list_filter 中的每个过滤器后显示相关对象的计数?

class Application(TimeStampModel):

    name = models.CharField(verbose_name='CI Name', max_length=100, unique=True)
    description = models.TextField(blank=True, help_text="Business application")

class Server(TimeStampModel):
    name = models.CharField(max_length=100, verbose_name='Server Name', unique=True)
    company = models.CharField(max_length=3, choices=constants.COMPANIES.items())
    online = models.BooleanField(default=True, blank=True, verbose_name='OnLine')
    application_members = models.ManyToManyField('Application',through='Rolemembership',
            through_fields = ('server', 'application'),
            )

 
class Rolemembership(TimeStampModel):

    server = models.ForeignKey(Server, on_delete = models.CASCADE)
    application = models.ForeignKey(Application, on_delete = models.CASCADE)
    name = models.CharField(verbose_name='Server Role', max_length=50, choices=constants.SERVER_ROLE.items())
    roleversion = models.CharField(max_length=100, verbose_name='Version', blank=True)

管理员.py

@admin.register(Server)
class ServerAdmin(admin.ModelAdmin):

    save_on_top = True
    list_per_page = 30
    list_max_show_all = 500
    inlines = [ServerInLine]

    list_filter = (
        'region',
        'rolemembership__name',
        'online',
        'company',
        'location',
        'updated_on',
    )

即在列表过滤器中的每个过滤器之后,我想显示相关对象的计数。

现在它只显示过滤器列表,即位置过滤器列表

  • 多伦多
  • 纽约
  • 芝加哥

我希望过滤器显示如下计数:

  • 多伦多(5)
  • 纽约(3)
  • 芝加哥(2)

如果过滤器有 0 个相关对象,则不显示过滤器。

4

2 回答 2

6

这可以通过结合两个想法使用自定义列表过滤器来实现。

一:该lookups方法允许您控制查询字符串中使用的值以及显示为过滤文本的文本。

二:在构建过滤器列表时可以检查数据集。https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter上的文档显示了开始十年列表过滤器的示例(始终显示«80s»和«90s »)和一个动态过滤器(如果有匹配的记录,则显示 «80s»,对于 «90s» 相同)。

同样为方便起见,将 ModelAdmin 对象传递给查找方法,例如,如果您希望基于可用数据进行查找

这是我编写的一个过滤器,用于按语言过滤数据:

class BaseLanguageFilter(admin.SimpleListFilter):
    title = _('language')
    parameter_name = 'lang'

    def lookups(self, request, model_admin):
        # Inspect the existing data to return e.g. ('fr', 'français (11)')
        # Note: the values and count are computed from the full data set,
        # ignoring currently applied filters.
        qs = model_admin.get_queryset(request)
        for lang, name in settings.LANGUAGES:
            count = qs.filter(language=lang).count()
            if count:
                yield (lang, f'{name} ({count})')

    def queryset(self, request, queryset):
        # Apply the filter selected, if any
        lang = self.value()
        if lang:
            return queryset.filter(language=lang)

您可以从那里开始并通过将部分替换为查询settings.LANGUAGES集聚合 + values_list 来适应您的城市,这将返回城市的不同值和计数。

于 2018-04-18T21:58:03.927 回答
1

Éric 的代码为我提供了 80% 的所需内容。为了解决他在代码中留下的评论(关于忽略当前应用的过滤器),我最终在我的用例中使用了以下内容:

from django.db.models import Count

class CountAnnotatedFeedFilter(admin.SimpleListFilter):
    title = 'feed'
    parameter_name = 'feed'

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request).filter(**request.GET.dict())
        for pk, name, count in qs.values_list('feed__feed_id', 'feed__feed_name').annotate(total=Count('feed')).order_by('-total'):
            if count:
                yield pk, f'{name} ({count})'

    def queryset(self, request, queryset):
        feed_id = self.value()
        if feed_id:
            return queryset.filter(feed_id=feed_id)

然后,在管理模型中:

class FeedEntryAdmin(admin.ModelAdmin):
    list_filter = (CountAnnotatedFeedFilter,)

注意:正如 Éric 还提到的,这会严重影响管理面板的速度,因为它可能必须执行昂贵的查询。

于 2020-11-23T15:45:07.483 回答