You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Django REST Framework查询参数实现产品列表按名称过滤

问题根源分析

  1. 路由配置错误:DRF的DefaultRouter注册视图集时会自动生成标准REST风格路由,你额外注册的Products/(?P<name>.+)/$规则会把路径中的值当作视图集默认retrieve方法的主键参数,触发单实例查询逻辑,根本不会走你写的get_queryset过滤代码,所以访问/api/Products/guitar/无结果。
  2. 查询参数报错原因:
    • 直接用f字符串拼接RAW SQL存在SQL注入风险,且如果name参数携带单引号等特殊字符会直接触发SQL语法错误
    • 关联查询时未指定name字段所属表,如果Product和Shop表都有name字段会触发数据库字段歧义错误
    • 你访问时路径带的尾斜杠会被带入name参数值,导致匹配不到对应数据

修复方案

第一步:修正路由配置

删除多余的路由注册,查询参数无需额外配置路由,DRF会自动从请求中提取:

router = routers.DefaultRouter()                   
router.register(r'Products', views.ProductView, 'Products')  

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls))
]

第二步:优化视图查询逻辑

优先使用Django ORM实现关联查询,避免手写RAW SQL的安全和语法问题,假设Product模型通过id_shop外键关联Shop模型:

def get_queryset(self):
    queryset = Product.objects.select_related('shop').all()[:12]
    name = self.request.query_params.get('name')
    if name is not None:
        # 去除参数末尾多余的斜杠
        name = name.rstrip('/')
        queryset = Product.objects.select_related('shop').filter(name__icontains=name)[:12]
    return queryset

如果必须使用RAW SQL,一定要用参数化查询避免注入和语法错误:

def get_queryset(self):
    name = self.request.query_params.get('name')
    if name is not None:
        name = name.rstrip('/')
        # 用占位符传递参数,Django会自动转义特殊字符,同时明确指定name所属表
        queryset = ProductJoinShop.objects.raw(
            'select * from Product join Shop using (id_shop) where Product.name like %s limit 12',
            [f'%{name}%']
        )
    else:
        queryset = ProductJoinShop.objects.raw('select * from Product join Shop using (id_shop) limit 12')
    return queryset

第三步(可选):支持路径格式的查询接口

如果需要兼容/api/Products/guitar/这种路径传参的形式,给视图集添加自定义action即可:

from rest_framework.decorators import action
from rest_framework.response import Response

class ProductView(viewsets.ModelViewSet):
    serializer_class = ProductSerializer
    # 保留上面修改后的get_queryset方法

    @action(detail=False, methods=['get'], url_path='(?P<name>.+)')
    def search_by_name(self, request, name=None):
        name = name.rstrip('/')
        queryset = Product.objects.select_related('shop').filter(name__icontains=name)[:12]
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

修改完成后,/api/Products/?name=guitar和/api/Products/guitar两种访问方式都可以正常返回过滤结果。

内容的提问来源于stack exchange,提问作者Rami16

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 03:06:01