ViewSets: 提供一组操作行为action,例如:list(),retrieve(),create(),update(),destory()等,不再提供get,post方法了。其下多种ModelViewSet能提供更简便的开发方式。
注册路由:自动处理将资源连接到视图和URL的约定。将不同http请求与ViewSets不同方法,一一映射起来。
重构我们UserList和UserDetail意见纳入一个单一的UserViewSet。我们可以删除这两个视图,并用一个类替换它们:
# class UserList(generics.ListAPIView):
# queryset = User.objects.all()
# serializer_class = UserSerializer
# class UserDetail(generics.RetrieveAPIView):
# queryset = User.objects.all()
# serializer_class = UserSerializer
# ViewSets
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# This viewset automatically provides `list` and `detail` actions.
重构SnippetList,SnippetDetail和SnippetHighlight视图类,用一个类替换它们。
# class SnippetList(generics.ListCreateAPIView):
# queryset = Snippet.objects.all()
# serializer_class = SnippetSerializer
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#
# def perform_create(self, serializer):
# # 保存之前按,加入owner信息
# # 通过覆盖我们的代码段视图上的方法,允许我们修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。
# serializer.save(owner=self.request.user)
#
#
# class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
# queryset = Snippet.objects.all()
# serializer_class = SnippetSerializer
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly)
#
#
# class SnippetHighlight(generics.GenericAPIView):
# queryset = Snippet.objects.all()
# renderer_classes = (renderers.StaticHTMLRenderer,)
#
# def get(self, request, *args, **kwargs):
# snippet = self.get_object()
# return Response(snippet.highlighted)
# 修改SnippetList, SnippetDetail and SnippetHighlight,成为一个类
from rest_framework.decorators import action
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,)
@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装饰器:
这里,我们主要探求ViewSet如何通过将http方法绑定到每个视图所需的操作的。
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'
})
将资源绑定到具体视图中,我们可以像往常一样使用URL conf注册视图。
urlpatterns = format_suffix_patterns([
path('', api_root),
path('snippets/', snippet_list, name='snippet-list'),
path('snippets//', snippet_detail, name='snippet-detail'),
path('snippets//highlight/', snippet_highlight, name='snippet-highlight'),
path('users/', user_list, name='user-list'),
path('users//', user_detail, name='user-detail')
])
# 这里注意,path(route, view, kwargs=None, name=None)参数,view填写上面我们生成的视图
因为我们使用的是ViewSet类而不是View类,所以我们实际上不需要自己设计URL。可以使用Router类自动处理将资源连接到视图和URL的约定。
这是我们的重写snippets/urls.py文件。
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views
# 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.
urlpatterns = [
path('', include(router.urls)),
]
向路由器注册视图集与提供urlpattern类似。我们包括两个参数 - 视图的URL前缀和视图集本身。
我们正在使用的DefaultRouter类也会自动为我们创建api_root根视图,因此我们可以删除掉# snippets/views
模块中的api_root方法而不会有任何影响。
# snippets/views.py
# # 給API_ROOT 添加API 端点
# @api_view(['GET'])
# def api_root(request, format=None):
# return Response({
# 'users': reverse('user-list', request=request, format=format),
# 'snippets': reverse('snippet-list', request=request, format=format)
# })
视图与视图集之间的权衡
使用视图集可能是一个非常有用的抽象。它有助于确保URL约定在您的API中保持一致,最大限度地减少您需要编写的代码量,并使您可以专注于API提供的交互和表示,而不是URL conf的细节
但是,使用视图集不如单独构建视图那么明确。