在DRF中嵌套ViewSet路由

时间:2016-11-06 22:22:49

标签: django django-rest-framework

我已经创建了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

我的路由器/网址配置如下所示

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这样的图书馆,但这对我(相当简单)的需求来说似乎有些过分。

4 个答案:

答案 0 :(得分:3)

使用列表路径定义管理视图集。这些参数将允许您执行具有指定权限(经过身份验证并具有管理权限)的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引用不会与您的详细路径冲突。

答案 1 :(得分:1)

我想我已经找到了自己问题的答案,但是如果有人想要加入(@ tom-christie可能吗?),我会在此处加上+50代表赏金。

无论哪种方式,我使用@list_routeAdminViewSet的{​​{1}}函数解决了它的问题。

这样的东西就足够了:

.as_view()

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

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