Django如何将动态URL参数传递到queryset实现数据过滤
解决方案
核心问题说明
你的代码存在4个典型错误:
- 缺失
rest_framework.viewsets的导入声明,代码运行时会直接抛出未定义异常 - 类属性级别定义的
queryset会在模块加载时就执行,此时无法获取请求时的动态URL参数,写死的过滤逻辑只会生效一次 - 错误重写
ViewSet的__init__方法获取参数,DRF的所有路径参数都会默认存放在视图实例的self.kwargs字典中,不需要重写构造方法 - DRF的
DefaultRouter.register方法不支持前缀中直接写路径参数,不能用路由器注册带动态参数的路径
可选修正方案
方案1:独立视图单独配置路由
第一步:修改views.py
from rest_framework import permissions, viewsets # 补全缺失的导入 from .serializers import ProductSerializer,CategorySerializer from .models import Product,Category from rest_framework.response import Response from rest_framework.views import APIView class ProductViewSet(viewsets.ModelViewSet): queryset=Product.objects.all() serializer_class= ProductSerializer class CategoryViewSet(viewsets.ModelViewSet): queryset=Category.objects.all() serializer_class= CategorySerializer class ProductbyCategory(viewsets.ModelViewSet): serializer_class= ProductSerializer # 重写get_queryset方法实现动态过滤,每次请求都会执行该方法 def get_queryset(self): categoryid = self.kwargs.get('categoryid') return Product.objects.filter(categoryid=categoryid)
第二步:修改urls.py
from django.urls import path, include from .views import ProductViewSet,CategoryViewSet,ProductbyCategory from rest_framework import routers router = routers.DefaultRouter() router.register(r'products', ProductViewSet) router.register(r'category', CategoryViewSet) # 删除原来的带参数的router.register语句,直接在urlpatterns中配置路径 urlpatterns = [ path('',include(router.urls)), # 手动配置动态路径,绑定get请求到list方法 path('sort-by-category/<int:categoryid>/', ProductbyCategory.as_view({'get': 'list'}), name='product-by-category'), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), ]
方案2:使用action装饰器(更符合DRF最佳实践,代码更简洁)
不需要单独写ProductbyCategory视图,直接在已有ProductViewSet上扩展接口:
第一步:修改views.py
from rest_framework import permissions, viewsets from rest_framework.decorators import action # 导入action装饰器 from .serializers import ProductSerializer,CategorySerializer from .models import Product,Category from rest_framework.response import Response class ProductViewSet(viewsets.ModelViewSet): queryset=Product.objects.all() serializer_class= ProductSerializer # 添加自定义动作,detail=False表示是列表级接口,url_path定义路径规则 @action(detail=False, methods=['get'], url_path='sort-by-category/(?P<categoryid>\d+)') def filter_by_category(self, request, categoryid=None): filtered_products = self.get_queryset().filter(categoryid=categoryid) serializer = self.get_serializer(filtered_products, many=True) return Response(serializer.data) class CategoryViewSet(viewsets.ModelViewSet): queryset=Category.objects.all() serializer_class= CategorySerializer
第二步:修改urls.py
不需要额外加配置,保持原来的路由器注册即可:
from django.urls import path, include from .views import ProductViewSet,CategoryViewSet from rest_framework import routers router = routers.DefaultRouter() router.register(r'products', ProductViewSet) router.register(r'category', CategoryViewSet) urlpatterns = [ path('',include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), ]
修改后访问路径为/products/sort-by-category/对应分类ID/即可获取对应分类的产品列表。
内容的提问来源于stack exchange,提问作者Phan Quang Thang
相关产品推荐
相关产品推荐

