1. 程式人生 > >6 - Viewsets and routers

6 - Viewsets and routers

教程 6:ViewSets 和路由器

REST framework 包括一個用於處理 ViewSets 的抽象,它允許開發人員集中精力對 API 的狀態和互動進行建模,並保留 URL 結構,根據通用約定自動處理。

ViewSet 類與 View 類幾乎相同,只是它們提供諸如 readupdate 等操作,而不提供諸如 getput 等方法處理程式。

一個 ViewSet 類最後時刻只繫結一組方法處理程式,當它被例項化為一組檢視時,通常通過使用一個 Router 類來處理定義複雜的 url。

使用 ViewSets 重構

我們來看看當前的一組檢視,並將它們重構為檢視集。

首先讓我們將 UserListUserDetail 檢視重構為單個 UserViewSet。我們可以刪除這兩個檢視,並用一個類替換它們:

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    這個檢視集自動提供 `list` 和 `detail` 操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

這裡我們使用了 ReadOnlyModelViewSet 類來自動提供預設的 “只讀” 操作。我們仍然像我們使用常規檢視時一樣設定 queryset

serializer_class 屬性,但我們不再需要為兩個單獨的類提供相同的資訊。

接下來我們將替換 SnippetListSnippetDetailSnippetHighlight 檢視類。我們可以刪除這三個檢視,並再次用一個類替換它們。

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 檔案中,我們將 ViewSet 類繫結到一組具體檢視中。

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 類建立多個檢視,通過將 http 方法繫結到每個檢視所需的操作中。

現在我們已經將資源繫結到具體的檢視中,我們可以像往常一樣在 URL conf 中註冊檢視。

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。將資源連線到檢視和 URL 的約定可以使用 Router 類自動處理。我們需要做的就是使用路由器註冊相應的檢視集,然後讓它執行其餘操作。

這是我們重新連線的 snippets/urls.py 檔案。

from django.conf.urls import url, 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)

# API URL 現在由路由器自動確定。
urlpatterns = [
    url(r'^', include(router.urls))
]

用路由器註冊檢視集類似於提供 urlpattern。我們包括兩個引數——檢視的 URL 字首和檢視集本身。

我們使用的 DefaultRouter 類也為我們自動建立了 API 根檢視,所以我們現在可以從 views 模組中刪除 api_root 方法。

檢視與檢視集之間的權衡

使用檢視集可以是一個非常有用的抽象。它有助於確保 URL 約定在您的 API 中保持一致,最大限度地減少編寫所需的程式碼量,並允許您專注於 API 提供的互動和表示,而不是 URL conf 的細節。

這並不意味著它總是正確的做法。當使用基於類的檢視而不是基於函式的檢視時,也有類似的權衡考慮。使用檢視集不像單獨構建檢視那樣明確。

本教程的第 7 部分中,我們將介紹如何新增 API 模式,以及如何使用客戶端庫或命令列工具與我們的 API 進行互動。