Django Admin中用Group By重写get_queryset报错求助
问题原因
你返回的是values()生成的字典类型QuerySet,但Django Admin的ModelAdmin默认要求返回模型实例的QuerySet。当Admin渲染list_display中的外键字段(order_unit、status)时,内部会自动调用select_related优化查询,而values()后的QuerySet不支持后续调用select_related,因此触发了该错误。
解决方案
推荐通过自定义变更列表模板的方式实现分组统计展示,这种方式更贴合汇总数据的展示需求,同时避开Admin对实例QuerySet的依赖。
步骤1:修改Admin类配置
取消注释自定义模板路径,并重写changelist_view方法获取分组统计数据:
@admin.register(models.OrderSummary) class OrderSummaryAdmin(admin.ModelAdmin): change_list_template = 'admin/order_summary_change_list.html' date_hierarchy = 'created' show_full_result_count = False list_filter = ( ('created', DateRangeFilter), ('order_unit__name', MultiSelectFilter), ('status__name', MultiSelectFilter), ) def changelist_view(self, request, extra_context=None): extra_context = extra_context or {} # 获取经过筛选后的基础查询集 qs = self.get_queryset(request) # 按订单单元、状态分组统计订单数量 grouped_data = qs.values( 'order_unit__id', 'order_unit__name', 'status__id', 'status__name' ).annotate(total=Count('id')).order_by('order_unit__name', 'status__name') # 将统计数据传入模板 extra_context['grouped_data'] = grouped_data return super().changelist_view(request, extra_context=extra_context)
步骤2:创建自定义变更列表模板
在项目模板目录下创建admin/order_summary_change_list.html,继承默认Admin模板并替换结果列表区域:
{% extends "admin/change_list.html" %} {% block result_list %} <div class="results"> <table> <thead> <tr> <th>订单单元</th> <th>状态</th> <th>总数量</th> </tr> </thead> <tbody> {% for item in grouped_data %} <tr> <td>{{ item.order_unit__name }}</td> <td>{{ item.status__name }}</td> <td>{{ item.total }}</td> </tr> {% empty %} <tr><td colspan="3">暂无匹配数据</td></tr> {% endfor %} </tbody> </table> </div> {% endblock %}
备选方案(小数据量场景)
如果数据量较小,也可以通过返回实例QuerySet+自定义显示字段的方式实现,但性能略差:
@admin.register(models.OrderSummary) class OrderSummaryAdmin(admin.ModelAdmin): date_hierarchy = 'created' show_full_result_count = False list_display = ("get_order_unit", "get_status", "total_count") list_filter = ( ('created', DateRangeFilter), ('order_unit__name', MultiSelectFilter), ('status__name', MultiSelectFilter), ) def get_queryset(self, request): qs = super().get_queryset(request) # 预缓存分组统计结果 self.stats_map = { (item['order_unit__id'], item['status__id']): item['total'] for item in qs.values('order_unit__id', 'status__id').annotate(total=Count('id')) } # 返回去重后的实例QuerySet(仅支持PostgreSQL) return qs.select_related('order_unit', 'status').distinct('order_unit__id', 'status__id') def get_order_unit(self, obj): return obj.order_unit.name get_order_unit.short_description = "订单单元" def get_status(self, obj): return obj.status.name get_status.short_description = "状态" def total_count(self, obj): return self.stats_map.get((obj.order_unit.id, obj.status.id), 0) total_count.short_description = "总数量"
内容的提问来源于stack exchange,提问作者haku
相关产品推荐
相关产品推荐

