REST框架包含要一个用于处理的抽象ViewSets,允许开发人员专注于建模API的状态和交互,并使用URL构造基于通用约定自动处于。
ViewSet类几乎与View类相同,除了它们提供诸如read或update之类的操作,而不是诸如get或者put之类的方法处理程序。
最后一个ViewSet类只绑定到一组方法处理程序,当它被实例化成一组视图的时候,通常通过使用一个Router类来处理自己定义的URLconf的复杂性。
使用ViewSets重构
将UserList和UserDetail视图重构为一个UserViewSet。
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
自动提供list和detail操作
"""
queryset = User.objects.all()
serializer_class = UserSerializer
ReadOnlyModelViewSet
类自动提供默认的只读操作。我们仍然像使用常规视图时那样设置request和serializer_calss属性,但我们不再需要向两个单独的类提供相同的信息。
同样的SnippetList,SnippetDetail和SnippetHighlight视图也可以整合在同一个视图中。
class SnippetViewSet(viewsets.ModelViewSet):
"""
此视图自动提供list, create, retrieve, update 和 destroy 操作
另外我们还提供了一个额外的highlight的操作
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
@action(detail=True, 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
获得完整的默认读写操作集。
注意,这个视图中我们还是用@action
装饰器来创建名为highlight的自定义操作。此装饰器用于添加任何不适合标准create/update/delete样式的自定义路由
默认情况下,使用@action
装饰器的自定义操作将相应GET请求。如果我们想要一个相应POST请求的动作,可以使用methods参数。
默认情况下,自定义操作的URL取决于方法名称本身。如果要更改url构造方式,可以为装饰器设置url_path关键字参数。
将ViewSets绑定到URL
处理程序方法尽在定义URLconf时绑定到操作,要了解幕后发生了什么,我们首先要从ViewSets中明确创建一组视图。
# snippets/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets.views import SnippetViewSet, UserViewSet
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'
})
urlpatterns = format_suffix_patterns([
path('snippets/', snippet_list, name='snippet-list'),
path('snippets/<int:pk>/', snippet_detail, name='snippet-detail'),
path('snippets/<int:pk>/highlight/', snippet_highlight, name='snippet-highlight'),
path('users/', user_list, name='user-list'),
path('users/<int:pk>/', user_detail, name='user-detail')
])
使用路由器
因为我们使用的是ViewSet类而不是View类,所以实际上不需要自己设计URL。可以使用Router类自动处理将资源链接到视图和URL的约定。我们需要做的就是用路由器注册适当的视图集,然后让它完成剩下的操作。
# snippets/urls.py
from django.urls import path
from django.conf.urls import include
from rest_framework.routers import DefaultRouter
from snippets import views
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)
urlpatterns = [
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include(router.urls)),
]
想路由器注册视图与提供urlpattern类似,我们包含两个参数:视图的URL前缀和视图本身。
我们使用的DefaultRouter类也会自动为我们创建API根视图,因此现在可以从views模块中删除api_root方法。
视图和视图集之间的权衡
使用视图集可以是一个非常有用的抽象。它将助于确保URL约定在API中保持一直,最大限度地减少编写所需的代码量,让你能够专注于API提供的交互和表示,而不是URLconf的细节。
但是这并不也为这采用视图集总是正确的方法,在使用基于类的视图而不是基于函数的视图时,有一个类似的权衡要考虑。