如何在DRF中从路由传递变量到ViewSet以复用同一视图集
如何在DRF中从路由传递变量到ViewSet以复用同一视图集
我完全懂你的痛点——想复用同一个ViewSet处理不同类型的内容,却卡在了router.register()只能接收类、不能直接传带参数的实例这个问题上。其实有几种简单且优雅的方法能解决这个需求,我给你详细说说:
方法一:用工厂函数动态生成ViewSet子类
你可以写一个工厂函数,根据传入的type参数返回一个带有该属性的ContentController子类,这样就能给不同路由注册不同的子类实例了:
# views.py from rest_framework import viewsets class ContentController(viewsets.ModelViewSet): # 先定义默认的type,或者留空让工厂函数赋值 type = None queryset = Content.objects.all() serializer_class = ContentSerializer def get_queryset(self): # 在业务逻辑里使用self.type过滤数据 if self.type: return self.queryset.filter(type=self.type) return self.queryset # 新增工厂函数 def create_content_controller(content_type): # 动态创建子类,设置type属性 class TypedContentController(ContentController): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.type = content_type return TypedContentController
然后在urls.py里这样注册路由:
from rest_framework.routers import SimpleRouter from .views import create_content_controller router = SimpleRouter(trailing_slash=False) # 给不同路由注册不同type的ViewSet子类 router.register("/books", create_content_controller("books"), basename="content-books") router.register("/movies", create_content_controller("movies"), basename="content-movies") urlpatterns = router.urls
方法二:用type()函数动态创建类
如果你觉得工厂函数有点啰嗦,也可以直接用Python内置的type()函数动态生成子类,效果是一样的:
# urls.py from rest_framework.routers import SimpleRouter from .views import ContentController router = SimpleRouter(trailing_slash=False) # 动态创建带type属性的子类 BooksController = type('BooksController', (ContentController,), {'type': 'books'}) MoviesController = type('MoviesController', (ContentController,), {'type': 'movies'}) router.register("/books", BooksController, basename="content-books") router.register("/movies", MoviesController, basename="content-movies") urlpatterns = router.urls
这种方式下,ContentController里可以直接通过self.type访问这个属性,不用修改__init__方法。
为什么不能直接传实例?
DRF的SimpleRouter(以及其他路由类)设计上是基于类来生成视图的,它会在请求到来时自动初始化视图实例。如果直接传实例,路由系统无法处理后续的请求初始化逻辑,所以只能接收类作为参数。
备注:内容来源于stack exchange,提问作者Kevin Renskers
相关产品推荐
相关产品推荐

