如何为DRF视图集的Action指定自定义查找字段?
解决DRF单个Action自定义查找字段的问题
DRF的@action装饰器本身并不支持lookup_field参数,所以你之前的配置不会生效。默认情况下,detail=True的Action会复用视图集的lookup_field(默认值为pk)生成URL中的捕获组。以下是两种无需修改整个视图集查找字段的解决方案:
方案一:自定义Router实现批量支持Action级别的lookup_field
通过自定义Router,我们可以让Action通过kwargs传递自定义的lookup_field,并自动替换URL中的捕获组:
from rest_framework.routers import DefaultRouter class CustomLookupRouter(DefaultRouter): def get_routes(self, viewset): routes = super().get_routes(viewset) for route in routes: # 仅处理detail类型的Action if route.detail and hasattr(viewset, route.name): action_method = getattr(viewset, route.name) # 检查Action是否配置了自定义lookup_field if hasattr(action_method, "kwargs") and "lookup_field" in action_method.kwargs: target_lookup = action_method.kwargs["lookup_field"] # 替换URL路径中的<pk>捕获组为自定义字段 route.url_path = route.url_path.replace("<pk>", f"<{target_lookup}>") return routes
然后在视图集中给Action添加kwargs参数指定自定义查找字段:
from rest_framework.viewsets import ModelViewSet from rest_framework.decorators import action class TestViewSet(ModelViewSet): queryset = TestModel.objects.all() serializer_class = TestSerializer @action( methods=["GET"], detail=True, url_name="something", url_path="something", kwargs={"lookup_field": "uuid"} # 这里指定自定义查找字段 ) def get_something(self, request, uuid=None): # 直接通过uuid参数查询对象 obj = self.get_queryset().get(uuid=uuid) # 后续业务逻辑 return Response(...)
最后使用自定义Router注册视图集:
router = CustomLookupRouter() router.register(r"test", TestViewSet)
此时生成的URL会是^test/(?P<uuid>[^/.]+)/something/$,完全符合预期。
方案二:手动重写视图集的get_extra_action_urls
如果只需要给单个Action做特殊处理,可以直接重写视图集的get_extra_action_urls方法,手动添加自定义URL:
from rest_framework.viewsets import ModelViewSet from rest_framework.decorators import action from django.urls import path class TestViewSet(ModelViewSet): queryset = TestModel.objects.all() serializer_class = TestSerializer def get_extra_action_urls(self): # 获取默认生成的额外Action URL default_urls = super().get_extra_action_urls() # 移除原有的get_something对应的URL filtered_urls = [url for url in default_urls if url.name != "test-something"] # 添加自定义URL,使用uuid作为捕获组 custom_url = path( r"<uuid>[^/.]+)/something/", self.get_something, name="test-something" ) filtered_urls.append(custom_url) return filtered_urls @action( methods=["GET"], detail=True, url_name="something", url_path="something", ) def get_something(self, request, uuid=None): obj = self.get_queryset().get(uuid=uuid) # 后续业务逻辑 return Response(...)
这种方式更适合仅需修改单个Action的场景,无需自定义Router。
补充:确保对象查询正确
如果你的视图集默认使用pk查找对象,在自定义Action中需要通过uuid查询时,可以重写get_object方法做针对性处理:
def get_object(self): if self.action == "get_something": uuid = self.kwargs.get("uuid") return self.get_queryset().get(uuid=uuid) return super().get_object()
内容的提问来源于stack exchange,提问作者nefrob
相关产品推荐
相关产品推荐

