如何使用Django REST framework/django-filter向前端传递可用筛选选项
DRF + django-filter 返回可用筛选选项的最佳实践
这是非常常见的动态筛选场景,目前有两种符合DRF、django-filter规范的实现路径,也有成熟的第三方库可以直接使用:
方案1:直接使用成熟第三方库
你可以直接使用drf-facets这类专门的扩展库,它原生适配django-filter的过滤规则,会自动基于当前已过滤的查询集,统计每个筛选字段的可选值、对应匹配数量,你只需要做简单配置就可以将可用筛选选项注入到API响应中,完全符合框架规范,不用自己手写聚合逻辑。
方案2:自行实现(无额外依赖)
你提到的两种实现思路都是符合DRF设计规范的,可根据业务场景选择:
路径A:自定义Metadata类(OPTIONS请求返回)
这种方案将可用筛选选项作为API元数据返回,和业务数据逻辑分离,适合需要统一管理接口元数据的场景,示例代码如下:
from rest_framework.metadata import BaseMetadata class FilterOptionsMetadata(BaseMetadata): def determine_metadata(self, request, view): metadata = super().determine_metadata(request, view) # 拿到经过django-filter过滤后的最终查询集 filtered_queryset = view.filter_queryset(view.get_queryset()) # 按你的筛选字段配置,统计各字段可用值 available_filters = { "color": list(filtered_queryset.values_list("color", flat=True).distinct()), "size": list(filtered_queryset.values_list("size", flat=True).distinct()) } metadata["actions"]["GET"]["available_filters"] = available_filters return metadata
使用时只需要在对应视图中指定metadata_class = FilterOptionsMetadata,前端发起OPTIONS请求即可拿到当前查询条件下的可用筛选选项。
路径B:扩展分页响应(GET请求同业务数据返回)
这种方案不需要前端额外发起请求,拉取列表数据的同时就能拿到可用筛选选项,适合高频筛选的交互场景,示例代码如下:
from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response class FilterWithOptionsPagination(PageNumberPagination): def get_paginated_response(self, data): filtered_queryset = self.page.object_list # 统计可用筛选选项 available_filters = { "color": list(filtered_queryset.values_list("color", flat=True).distinct()), "size": list(filtered_queryset.values_list("size", flat=True).distinct()) } return Response({ "count": self.page.paginator.count, "next": self.get_next_link(), "previous": self.get_previous_link(), "available_filters": available_filters, "results": data })
使用时只需要在对应视图中指定pagination_class = FilterWithOptionsPagination即可。
性能注意事项
如果你的查询集数据量很大,直接对筛选字段做distinct聚合会有明显性能损耗,建议给对应的筛选字段添加数据库索引;如果量级达到百万以上,推荐对接Elasticsearch这类搜索引擎做聚合查询,django-filter支持对接ES查询后端,聚合效率会高很多。
内容的提问来源于stack exchange,提问作者thorsten
相关产品推荐
相关产品推荐

