Django REST framework 教程6 ViewSets & Routers

Posted by eckid on June 29, 2016

Tutorial 6: ViewSets & Routers

REST framework 的 ViewSets类帮助我们专注于设计API的状态和交互,并且自动的处理URL 的结构。ViewSet 类和 View 类的差别仅在于它提供了 readupdate方法,替代了原来的 get 或 put方法。 ViewSet 类最终只是关联了一系列方法,在实例化之后需要用 Router 来绑定URL。

用ViewSets重构视图

让我们重构视图,首先将 UserList 和 UserDetail 视图替换为一个 UserViewSet

from rest_framework import viewsets

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    This viewset automatically provides `list` and `detail` actions.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

我们用 ReadOnlyModelViewSet 类提供了一个默认为只读的视图, queryset 和serializer_class 属性和之前的一样,没有改变。但是不需要将下面的视图分割为多个类来呈现。让我们替换掉 SnippetListSnippetDetail 以及SnippetHighlight 类,用一个类表示:

from rest_framework.decorators import detail_route

class SnippetViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.

    Additionally we also provide an extra `highlight` action.
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)

    @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

我们用 ModelViewSet 类来提供默认的读写操作。

注意到我们用了 @detail_route 装饰器定义了highlight操作,这个装饰器适用于创建 create/update/delete 以外的自定义操作。 @detail_route 装饰器默认处理 GET 请求,可以修改其 methods参数来处理 POST 请求。

自定义操作的URL默认关联与它的命名,如需修改就把url_path做为参数传入装饰器。

把ViewSets 与URLs关联

视图只有绑定到 URLConf才能有作用,在 urls.py 文件中:

from snippets.views import SnippetViewSet, UserViewSet, api_root
from rest_framework import renderers

snippet_list = SnippetViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
    'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])
user_list = UserViewSet.as_view({
    'get': 'list'
})
user_detail = UserViewSet.as_view({
    'get': 'retrieve'
})

请观察我们怎样将每个 ViewSet 类视图绑定http方法。现在把视图注册到URL。

urlpatterns = format_suffix_patterns([
    url(r'^$', api_root),
    url(r'^snippets/$', snippet_list, name='snippet-list'),
    url(r'^snippets/(?P<pk>[0-9]+)/$', snippet_detail, name='snippet-detail'),
    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),
    url(r'^users/$', user_list, name='user-list'),
    url(r'^users/(?P<pk>[0-9]+)/$', user_detail, name='user-detail')
])

使用Routers

由于我们用的是 ViewSet 类,所以不需要自己注册URL,通过 Router类可以自动处理关联,我们只需要将正确的view注册到router中即可。在urls.py 中:

from django.conf.urls import url, include
from snippets import views
from rest_framework.routers import DefaultRouter

# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)

# The API URLs are now determined automatically by the router.
# Additionally, we include the login URLs for the browsable API.
urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

当然,使用router也要设置url模式串。 DefaultRouter 类会自动创建API的根视图,所以可以把api_root 方法从 views 中删去。

权衡views和viewsets

viewsets对视图创建进行和很好的抽象,它提供了统一的URL地址,极少的代码量,能让我们专注于API本身,省掉了配置URL的精力。但它也不是所有情况下都最适用的。相比based views,使用viewsets会让视图变得不易于理解。

回顾

我们构建了一个完整的可视化API,具有认证机制,对象权限以及支持多种格式。让我们回顾一下设计的每一个步骤,想想逐步简化视图的过程。

最终的代码 tutorial code 位于 GitHub, 或者访问 the sandbox.

继续向前

教程已经进入了尾声,面对接下来的REST framework 项目, 可以从以下方式继续学习: • 在 GitHub 上一起优化REST framework. • 加入讨论组 REST framework discussion group,成为社区的一员 • 用Twitter和作者交流 the author  Now go build awesome things.