此文档为官方文档的翻译,原文地址:https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/
REST框架包括一个用于处理ViewSets的抽象,它允许开发人员集中精力对API的状态和交互进行建模,并根据常规约定自动处理URL映射。
ViewSet类与View类几乎相同,不同之处在于它们提供诸如retrieve或update之类的操作,而不是get或put等方法。(注意:操作比方法的抽象等级更高)
ViewSet类仅在最后一刻绑定到一组方法处理程序,当它被实例化为一组视图时,通常使用 Router类来处理定义 URL映射的复杂操作。
使用ViewSets重构
首先,让我们将 UserList 和 UserDetail视图重构为单个 UserViewSet。我们可以删除这两个视图,并用一个类替换它们:
from rest_framework import viewsetsclass UserViewSet(viewsets.ReadOnlyModelViewSet):"""此视集自动提供“列表”和“检索”操作。"""queryset = User.objects.all()serializer_class = UserSerializer
在这里,我们使用 ReadOnlyModelViewSet 类自动提供默认的“只读”操作。我们仍然像使用常规视图时那样设置queryset 和serializer_class 属性,但是我们不再需要向两个独立的类提供相同的信息。(这波操作属于又做了一层抽象!减少任何重复语句!)
接下来我们将替换SnippetList、 SnippetDetail 和 SnippetHighlight 视图类。我们可以删除这三个视图,然后再用一个类替换它们。
from rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework import permissionsclass SnippetViewSet(viewsets.ModelViewSet):"""这个视图集自动提供“列表”、“创建”、“检索”,“更新”和“删除”操作。(牛逼!一个视图覆盖了增、删、改、查和列表!!!)此外,我们还提供额外的“高亮”动作。"""queryset = Snippet.objects.all()serializer_class = SnippetSerializerpermission_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)、删(destroy)、改(update\partial_update)、查(retrieve)、列表(list))。该装饰器可用于添加任何不适合标准create/update/delete操作的自定义端点。
默认情况下,使用@action 装饰器的自定义操作将响应GET请求。如果我们想要一个响应POST请求的动作,我们可以使用methods参数传入一个请求类型的list来设置所有允许的请求类型。
默认情况下自定义操作的 url 取决于方法名称本身。如果您希望更改 url 的构造方式,可以为装饰器设置url_path关键字参数。
显式地将 ViewSets 绑定到 url
只有在定义 URLConf 时,处理程序方法才会绑定到操作。为了了解在幕后发生的事情,让我们首先显式地从 ViewSets 创建一组视图。
在 snippets/urls.py 文件中,我们将ViewSet 类绑定到一组具体视图中。
from snippets.views import SnippetViewSet, UserViewSet, api_rootfrom rest_framework import rendererssnippet_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([path('', api_root),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 配置。将资源连接到视图和 url 的常规操作可以使用Router类自动处理。我们所需要做的就是向路由器注册合适的视图集,然后让它完成剩下的工作。
这是我们重写的snippets/urls.py文件。
from django.urls import path, includefrom rest_framework.routers import DefaultRouter # 导入路由器from snippets import views# 创建路由器并用它注册我们的视图集。router = DefaultRouter()router.register(r'snippets', views.SnippetViewSet, basename="snippet") # 官网这地方的basename加了s,错了!这里的basename应该和模型名完全一致!(除了大小写)router.register(r'users', views.UserViewSet, basename="user")# 现在由路由器自动确定API URL。urlpatterns = [path('', include(router.urls)),]
在路由器上注册视图集类似于提供一个 urlpattern。我们包括两个参数 — 视图的 URL 前缀和视图集本身。
我们正在使用的 DefaultRouter 类也会自动为我们创建 API 根视图,因此我们现在可以从我们的视图模块中删除 api_root 方法。
视图与视图集之间的权衡
使用视图集可以是一个非常有用的抽象。它有助于确保 URL 约定在你的 API 中保持一致,最大限度地减少你需要编写的代码数量,并且允许你专注于你的 API 提供的交互和表示,而不是 URL 配置的细节。
这并不意味着这始终是正确的方法。就像当使用基于类的视图而不是基于函数的视图时,也有一组类似的权衡值得考虑。使用视图集比单独构建您的视图的明确性要少。
