如何在Django Admin的list_filter中批量实现关联外键对象计数显示?
实现通用的带数量统计的Django Admin筛选器
你已经搞定了单个外键的筛选器统计,现在我们可以把这个逻辑封装成一个通用的基类,这样所有需要显示关联数量的筛选器都能直接复用,不用重复写代码啦!
第一步:创建通用的筛选器基类
我们可以写一个继承自admin.SimpleListFilter的通用类,通过传入字段名来动态生成筛选逻辑:
from django.contrib import admin from django.db.models import Count from django.utils.translation import gettext_lazy as _ class CountedForeignKeyFilter(admin.SimpleListFilter): # 可以通过子类或者实例化时指定的参数 field_name = None label_field = 'name' # 默认外键模型的显示字段是name,可自定义 title = None def __init__(self, request, params, model, model_admin): super().__init__(request, params, model, model_admin) # 如果没有指定title,自动用字段名的人性化翻译 if not self.title: self.title = _(self.field_name.replace('_', ' ').capitalize()) # 参数名默认用字段名 self.parameter_name = self.field_name def lookups(self, request, model_admin): if not self.field_name: raise ValueError("必须为CountedForeignKeyFilter指定field_name参数") qs = model_admin.get_queryset(request) # 动态构建values_list和annotate的字段 id_field = f"{self.field_name}__id" label_field = f"{self.field_name}__{self.label_field}" # 聚合统计每个外键关联的数量 results = qs.values_list(id_field, label_field).annotate( total=Count(self.field_name) ).order_by('-total') for pk, name, count in results: if count: yield (pk, f'{name} ({count})') def queryset(self, request, queryset): id_val = self.value() if id_val: return queryset.filter(**{self.field_name: id_val}) return queryset
第二步:在ModelAdmin中使用这个通用筛选器
现在你不用再为每个字段写单独的筛选器类了,直接在list_filter里实例化这个通用类,传入对应的字段名就行:
单个字段的用法
class CaseAdmin(admin.ModelAdmin): list_filter = ( lambda: CountedForeignKeyFilter(field_name='client'), # 如果你需要自定义外键的显示字段,比如client用full_name而不是name: # lambda: CountedForeignKeyFilter(field_name='client', label_field='full_name'), )
多个外键字段的用法
如果你的模型还有其他外键字段,比如category、assigned_user,直接添加即可:
class CaseAdmin(admin.ModelAdmin): list_filter = ( lambda: CountedForeignKeyFilter(field_name='client'), lambda: CountedForeignKeyFilter(field_name='category', label_field='title'), lambda: CountedForeignKeyFilter(field_name='assigned_user', label_field='username'), )
你也可以用创建子类的方式(适合需要多次复用的字段):
class ClientCountFilter(CountedForeignKeyFilter): field_name = 'client' label_field = 'full_name' class CaseAdmin(admin.ModelAdmin): list_filter = (ClientCountFilter,)
逻辑说明
- 动态字段处理:通过
field_name参数,自动拼接外键的ID字段和显示字段(比如client__id、client__name),不用硬编码。 - 数量统计:利用
annotate(Count(self.field_name))统计每个外键关联的当前模型记录数。 - 灵活配置:可以通过
label_field参数自定义外键模型的显示字段,适配不同的模型结构。
这样一来,你所有需要显示关联数量的筛选器都能复用这个逻辑,不用再重复写相似的代码啦!
内容的提问来源于stack exchange,提问作者jerrymouse
相关产品推荐
相关产品推荐

