教程6:ViewSets和Routers

REST framework提供了一种叫做ViewSets的抽象行为,它可以使开发人员聚焦于API的状态和实现,基于常见的约定而自动进行URL配置。

ViewSetView很像,除了它提供的是read以及update操作而不是HTTP的get或者post

ViewSet仅在被调用的时候才会和对应的方法进行绑定,当它被实例化时——通常是在使用Route类管理URL配置的时候。

使用ViewSet重构代码

首先我们使用但一个UserViewSet来取代UserListUserDetail,我们先移除那两个类,并添加:

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自动提供了“只读”方法,并且依然想使用常规视图是那样设置了querysetseriallizer_class属性,但我们不需要写2个类了。

下面我们修改SnippetListSnippetDetailSnippetHighlight类,同样删除它们并修改成一个类:

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装饰器来创建自定义动作,这个装饰器可以用于任何不符合标准create/update/delete的动作。

使用@detail_route装饰的用户自定义动作默认相应GET请求,如果需要响应POST操作需要指定methods参数。

默认情况下,自定义动作对应的URL取决于它们的函数名,也可以通过给装饰器传递url_path参数来进行修改。

显式绑定URL和ViewSets

仅仅在我们定义URL配置时HTTP方法才会和我们定义的行为进行绑定。为了理解细节,我们先显式的在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都创建了多个View,并且为每个View的行为和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而不是View,实际上我们不需要自己定义URL。使用Router类可以自动的进行上述操作,我们需要做的仅仅是正确的注册View到Router中:

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'))
]

使用route生成了相同的URL路径,我们包含了2个参数——URL前缀以及Viewset本身。

DefaultRouter类自动创建了根URL,所以也可以在views中移除api_root了。

权衡Views和Viewsets

viewsets是一种很用的抽象,它帮助我们确保URL符合惯例,减少代码编写量,使你专注于API交互和设计而不是URL配置上。

但这并不意味这总是一种好的选择,就好象函数视图和类视图之间的权衡一样,使用viewsets相比于显示构建vews,有些隐晦。

总结

通过少量的代码,我们构建出一个完备的Web API,完美支持浏览器访问、用户认证、权限管理,并且支持多种返回格式。

我们经历了每步设计的过程,并且知道了如果我们需要自定义功能,可以方便的使用Django原生的views.

你可以在github上找到最终的代码,以及模拟程序

展望

整个教程到这就结束了,如果你想得到更多信息,这里有几点建议:

results matching ""

    No results matching ""