6 - Viewsets and routers
教程 6:ViewSets 和路由器
REST framework 包括一個用於處理 ViewSets
的抽象,它允許開發人員集中精力對 API 的狀態和互動進行建模,並保留 URL 結構,根據通用約定自動處理。
ViewSet
類與 View
類幾乎相同,只是它們提供諸如 read
或 update
等操作,而不提供諸如 get
或 put
等方法處理程式。
一個 ViewSet
類最後時刻只繫結一組方法處理程式,當它被例項化為一組檢視時,通常通過使用一個 Router
類來處理定義複雜的 url。
使用 ViewSets 重構
我們來看看當前的一組檢視,並將它們重構為檢視集。
首先讓我們將 UserList
和 UserDetail
檢視重構為單個 UserViewSet
。我們可以刪除這兩個檢視,並用一個類替換它們:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
這個檢視集自動提供 `list` 和 `detail` 操作。
"""
queryset = User.objects.all()
serializer_class = UserSerializer
這裡我們使用了 ReadOnlyModelViewSet
類來自動提供預設的 “只讀” 操作。我們仍然像我們使用常規檢視時一樣設定 queryset
serializer_class
屬性,但我們不再需要為兩個單獨的類提供相同的資訊。
接下來我們將替換 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
檔案中,我們將 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 進行互動。