如何在Django REST API中按subcategory字段过滤数据?
问题根源
你这段代码的核心问题是:虽然根据URL参数过滤出了符合条件的items,但最终返回的还是全量数据的序列化结果——因为你先对全量queryset做了序列化,后续过滤后的items完全没被用到,直接返回了初始的serializer.data。
修复后的视图代码
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from .models import Item from .serializers import ItemSerializer @api_view(['GET']) def view_items(request): # 初始化全量查询集 queryset = Item.objects.all() # 处理URL参数过滤 if request.query_params: # 只允许指定字段过滤,避免恶意参数注入 allowed_filters = ['category', 'subcategory'] filter_params = {k: v for k, v in request.query_params.dict().items() if k in allowed_filters} items = queryset.filter(**filter_params) else: items = queryset # 对过滤后的查询集做序列化 serializer = ItemSerializer(items, many=True) # 判断是否有结果返回 if items.exists(): return Response(serializer.data) else: return Response(status=status.HTTP_404_NOT_FOUND) @api_view(['GET']) def ApiOverview(request): api_urls = { 'all_items': '/', 'Search by Category': '/?category=category_name', 'Search by Subcategory': '/?subcategory=subcategory_name', } return Response(api_urls)
关键修改说明
- 把序列化步骤移到过滤逻辑之后,确保用过滤后的
items生成返回数据 - 增加了过滤字段白名单:直接用
**request.query_params.dict()存在安全风险(比如攻击者传入非法字段导致报错),只保留category和subcategory作为可过滤字段更稳妥 - 用
items.exists()替代直接判断if items:QuerySet做布尔判断时会执行全量查询,exists()只检查是否有数据,性能更优
进阶优化(可选)
如果后续需要更复杂的过滤规则(比如模糊查询、范围查询),可以用DRF官方推荐的django-filter工具:
- 安装依赖
pip install django-filter
- 在
settings.py中添加配置
INSTALLED_APPS = [ ... 'django_filters', 'rest_framework', ] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], }
- 定义过滤规则(新建
filters.py)
import django_filters from .models import Item class ItemFilter(django_filters.FilterSet): class Meta: model = Item fields = ['category', 'subcategory']
- 改用类视图(更符合DRF最佳实践)
from rest_framework.viewsets import ReadOnlyModelViewSet from .models import Item from .serializers import ItemSerializer from .filters import ItemFilter class ItemViewSet(ReadOnlyModelViewSet): queryset = Item.objects.all() serializer_class = ItemSerializer filterset_class = ItemFilter
- 更新URL配置
from rest_framework.routers import DefaultRouter from .views import ItemViewSet, ApiOverview router = DefaultRouter() router.register(r'all', ItemViewSet) urlpatterns = [ path('', ApiOverview, name='home'), ] + router.urls
内容的提问来源于stack exchange,提问作者mightycode Newton
相关产品推荐
相关产品推荐

