如何在DRF GenericViewSet、Mixin类视图中正确使用路径参数?
问题根源
你的代码无法生效是两个写法错误导致的:
- DRF的
@action装饰器的url_path参数不支持Djangopath()函数的<类型:参数名>转换器语法,传入的字符串会被直接拼接为正则路由片段,你写的<str:period>会被当作普通字符匹配,不会被识别为路径参数。 url_name参数是用于路由反向解析的别名,不能包含尖括号、转换器标记或空格,你传入的<str: period>属于无效值,会直接导致路由注册失败。
修复方案
你可以任选以下两种方式实现,都能满足localhost:8000/api/ranks/{period}的访问需求。
方案1:保留@action装饰器,用正则捕获参数
修改views.py中的@action配置,用正则命名分组捕获period参数即可:
from rest_framework import mixins, status from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action # 提前导入你的Record、RecordSerializer模型和序列化器 class RankingView(mixins.ListModelMixin, GenericViewSet): queryset = Record.objects.all() serializer_class = RecordSerializer # 修正url_path和url_name参数 @action( methods=['GET'], detail=False, # 用正则命名分组捕获period参数,[^/.]+表示匹配除了/和.之外的任意字符 url_path='(?P<period>[^/.]+)', url_name='rank-list' ) def get_rank(self, request, period): # 编写你的业务逻辑,示例:根据period过滤数据 filtered_records = self.get_queryset().filter(period=period) serializer = self.get_serializer(filtered_records, many=True) return Response(serializer.data, status=status.HTTP_200_OK)
urls.py不需要修改,保持原有写法即可。如果需要限制period的可选值(比如只允许daily/weekly/monthly),可以把正则改成(?P<period>daily|weekly|monthly),不符合规则的参数会直接返回404。
方案2:手动注册路由,使用熟悉的path转换器语法
如果觉得正则写法不好记,可以去掉@action装饰器,手动在urls.py中注册带参数的路由,更符合Django原生路由的写法习惯。
首先修改views.py,去掉@action装饰器,直接写视图方法:
from rest_framework import mixins, status from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet # 提前导入你的Record、RecordSerializer模型和序列化器 class RankingView(mixins.ListModelMixin, GenericViewSet): queryset = Record.objects.all() serializer_class = RecordSerializer def get_rank(self, request, period): # 业务逻辑和方案1一致 filtered_records = self.get_queryset().filter(period=period) serializer = self.get_serializer(filtered_records, many=True) return Response(serializer.data, status=status.HTTP_200_OK)
然后修改urls.py,手动追加带参数的路由映射:
from django.urls import path, include from rest_framework.routers import DefaultRouter # 导入你的RankingView from .views import RankingView router = DefaultRouter() router.register(r'api/ranks', RankingView) urlpatterns = [ path('', include(router.urls)), # 手动绑定带period参数的路由到get_rank方法 path( 'api/ranks/<str:period>', RankingView.as_view({'get': 'get_rank'}), name='rank-list' ) ]
验证方式
代码修改完成后,启动服务访问localhost:8000/api/ranks/weekly(把weekly替换为你需要传入的period值),即可正常进入对应视图方法拿到返回结果。
内容的提问来源于stack exchange,提问作者seongkyulim
相关产品推荐
相关产品推荐

