第 6 部分 - 视图集和路由器

Django REST 框架包含一个用于处理视图集的抽象层,这样开发者就可以集中精力去建模 API 的状态和交互,而将 URL 构建自动交给系统处理,基于常见的约定来完成。

视图集类和视图类几乎是一样的,只不过它们提供的是像 retrieve 或者 update 这样的操作,而不是像 get 或者 put 这样的方法处理程序。

当视图集类被实例化为一组视图时,才会在最后一刻与一组方法处理程序绑定,通常是由路由器类来处理定义 URL 配置的复杂性。

重构为使用视图集

让我们将当前的视图集重构为视图集。

首先,我们将 UserListUserDetail 类重构为一个单独的 UserViewSet 类。在 snippets/views.py 文件中,我们可以删除这两个视图类,并用一个单独的视图集类来替换它们:

from rest_framework import viewsets

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    此视图集自动提供 `list` 和 `retrieve` 操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

在这里,我们使用了 ReadOnlyModelViewSet 类来自动提供默认的 “只读” 操作。我们设置 querysetserializer_class 属性的方式与使用普通视图时完全一样,但不再需要将相同的信息提供给两个单独的类。

接下来,我们将替换 SnippetListSnippetDetailSnippetHighlight 视图类。我们可以删除这三个视图,并再次用一个单独的类来替换它们。

from rest_framework import permissions
from rest_framework import renderers
from rest_framework.decorators import action
from rest_framework.response import Response

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 作为关键字参数。

显式地将视图集绑定到 URL

处理器方法只有在定义 URLConf 时才会与操作绑定。

为了查看底层发生了什么情况,让我们首先从视图集显式地创建一组视图。

snippets/urls.py 文件中,我们将视图集类绑定到一组具体的视图。

from rest_framework import renderers

from snippets.views import api_root, SnippetViewSet, UserViewSet

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

注意到我们是如何通过将 HTTP 方法绑定到每个视图所需的操作来从每个视图集类创建多个视图的。

现在我们已经将资源绑定到了具体的视图上,就可以像往常一样将视图注册到 URL 配置中。

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

使用路由器

因为我们使用的是视图集类而不是视图类,所以我们实际上不需要自己设计 URL 配置。使用路由器类可以自动处理将资源连接到视图和 URL 的约定。我们所要做的就是将适当的视图集注册到路由器,然后让它来完成其余的工作。

以下是我们的重新连接的 snippets/urls.py 文件。

from django.urls import path, include
from rest_framework.routers import DefaultRouter

from snippets import views

# 创建一个路由器并注册我们的视图集。
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet, basename='snippet')
router.register(r'users', views.UserViewSet, basename='user')

# API URL 现在由路由器自动确定。
urlpatterns = [
    path('', include(router.urls)),
]

将视图集注册到路由器类似于提供一个 urlpattern。我们包含两个参数 —— 视图的 URL 前缀和视图集本身。

我们使用的 DefaultRouter 类还会自动为我们创建 API 根视图,因此我们现在可以从 views 模块中删除 api_root 函数。

视图与视图集之间的权衡

使用视图集可以是一个非常有用的抽象。它有助于确保 API 中 URL 约定的一致性,减少你需要编写的代码量,并使你能够专注于 API 提供的交互和表示,而不是 URL 配置的具体细节。

这并不意味着它总是正确的做法。在使用视图集而不是单独构建 API 视图时,需要考虑类似的权衡。

你可能感兴趣的:(pythondjango)