在DRF中嵌套ViewSet路由

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在DRF中嵌套ViewSet路由相关的知识,希望对你有一定的参考价值。

我已经创建了2个这样的ModelViewSets(简化为演示):

class SomeBaseViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = SomeEventSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        return SomeObjects.objects.filter(owner=self.request.user)

    def get_serializer_context(self):
        context = super(SomeBaseViewSet, self).get_serializer_context()
        context.update({
            "user": self.request.user,
            "some_context_key": False
        })
        return context

class AdminViewSet(SomeBaseViewSet):
    # Added in the HasAdminPermission
    permission_classes = (permissions.IsAuthenticated, HasAdminPermission)

    # Different queryset filter (overriding `get_queryset` vs setting queryset for standardization)
    def get_queryset(self):
        return SomeObjects.objects.all()

    # The context should have `some_context_key=True`, and `user=request.user`
    def get_serializer_context(self):
        context = super(AdminViewSet, self).get_serializer_context()
        context.update({
            "some_context_key": True
        })
        return context

我的路由器/ url配置看起来像这样

router = DefaultRouter()

router.register(r'some_view', SomeBaseViewSet, base_name="some_view")

urlpatterns += [
    url(r'^api/', include(router.urls)),
]

如果我想将/api/some_view/admin路由到AdminViewSet,那么最好的方法是什么?

我试过的事情:

  • @list_route上的SomeBaseViewSet,但无法弄清楚将它连接到我的AdminViewSet的“正确”方式
  • url(r'^api/some_view/admin$', AdminViewSet.as_view({"get": "list"}))添加到我的urlpatterns(这种类型可以运行,但是ViewSet稍微适用一些,并且通常真的是手动的):
  • 有一个专用的DefaultRouter用于some_view视图,然后我登上url(r'^api/some_view/') - 再次hacky和迂腐与大量的路线

是否有一种“正确”的方式来完成我想要完成的任务,或者我应该找到针对此问题的不同解决方案(即过滤器或其他东西)?

我见过像https://github.com/alanjds/drf-nested-routers这样的图书馆,但这对我(相当简单)的需求来说似乎有些过分。

答案

使用列表路径定义管理视图集。这些参数将允许您执行具有指定权限(经过身份验证并具有管理权限)的get请求,该请求扩展了此类。即/someview/adminsomeotherview/admin

from rest_framework.decorators import list_route
class AdminViewSet(viewset.Viewsets):

    @list_route(methods=['get'], 
                  permissions=[permissions.IsAuthenticated, HasAdminPermission],
                  url_path='admin'
     )
     def admin(self, request):
          # All your custom logic in regards to querysets and serializing
          return serialized.data

然后,您可以扩展需要管理操作路由的任何视图集。

class SomeBaseViewSet(viewsets.ReadOnlyModelViewSet, AdminViewset):
    serializer_class = SomeEventSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        return SomeObjects.objects.filter(owner=self.request.user)

    def get_serializer_context(self):
        context = super(SomeBaseViewSet, self).get_serializer_context()
        context.update({
            "user": self.request.user,
            "some_context_key": False
        })
        return context

你要小心这个,因为通常你的基本路线后面的参数,即/ someview / {param} /是为ID引用保留的。确保您的ID引用不会与您的详细路径冲突。

另一答案

我想我已经找到了我自己的问题的答案,但是如果有人想要加入(@ tom-christie也许?),我会在这个问题上加上+50代表奖金。

无论哪种方式,我为我的用例解决它的方式是使用@list_routeAdminViewSet.as_view()函数。

这样的东西就足够了:

class SomeBaseViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = SomeEventSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        return SomeObjects.objects.filter(owner=self.request.user)

    def get_serializer_context(self):
        context = super(SomeBaseViewSet, self).get_serializer_context()
        context.update({
            "user": self.request.user,
            "some_context_key": False
        })
        return context

    @list_route()
    def admin(self, request):
        return AdminViewSet.as_view({"get": "list"})(request)

class AdminViewSet(SomeBaseViewSet):
    # Added in the HasAdminPermission
    permission_classes = (permissions.IsAuthenticated, HasAdminPermission)

    # Different queryset filter (overriding `get_queryset` vs setting queryset for standardization)
    def get_queryset(self):
        return SomeObjects.objects.all()

    # The context should have `some_context_key=True`, and `user=request.user`
    def get_serializer_context(self):
        context = super(AdminViewSet, self).get_serializer_context()
        context.update({
            "some_context_key": True
        })
        return context

并允许人们相应地路由URL(基于函数的名称),并强制执行您需要的任何额外的事情。

另一答案

好问题。我为此检查了DRF的detail_route - 这是我过去成功使用的一个成语,用于创建一个挂起视图集的一次性端点。 HTH,

http://www.django-rest-framework.org/api-guide/routers/#extra-link-and-actions

另一答案

不知道我是否错过了一些东西,但我刚刚测试过,这完美无缺(订单很重要):

router = DefaultRouter()
# this will overrides routes from the line below
router.register(r'some_view/admin', AdminViewSet) 
router.register(r'some_view', SomeBaseViewSet)

以上是关于在DRF中嵌套ViewSet路由的主要内容,如果未能解决你的问题,请参考以下文章

RESTFramework(DRF)进阶篇GenericAPIview-ViewSet类

drf-路由

drf-路由和认证

drf-路由组件

05 drf路由组件

DRF中ManyToManyField的自定义序列化器和ViewSet [重复]