第 5 篇:用檢視集,簡化你的程式碼

削微寒發表於2020-05-15

作者:HelloGitHub-追夢人物

在 RESTful 架構中,對資源的常規操作無非就是查詢、新增、修改、刪除等這麼幾種。為此,django-rest-framework 分別提供了對應通用類檢視函式。但是,如果對同一個資源的不同操作邏輯分散在各個檢視函式中,從邏輯上來說不太合理,實際中管理起來也不是很方便,還會產生很多重複性的程式碼。因此,django-rest-framework 引入了檢視集(Viewsets),把對同一個資源的不同操作,集中到一個類中。同樣的,針對 Web 開發中的常見邏輯,django-rest-framework 也提供了通用檢視集,進一步簡化開發工作。

使用檢視集的一個更大的好處,就是可以配合 django-rest-framework 提供的路由器(router),自動生成 API 的 URL,不需要我們再手工將 URL 模式和檢視函式繫結了。所以大部分情況下,即使對資源只有一種操作,我們一般也會使用檢視集。

先來看看部落格首頁文章列表檢視集的程式碼:

blog/views.py

from rest_framework import viewsets
from rest_framework import mixins

class PostViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    serializer_class = PostListSerializer
    queryset = Post.objects.all()
    pagination_class = PageNumberPagination
    permission_classes = [AllowAny]

所有檢視集都要繼承檢視集的基類。檢視集也有 2 個基類:ViewSetGenericViewSet,前者是最基本的檢視集類,後者擴充自前者,擴充了很多 Web 開發中的通用邏輯。

要注意一點的是,檢視集基類提供的是除資源操作以外的通用邏輯(例如 HTTP 請求預處理、HTTP 響應後處理、認證、鑑權等),而對於資源的操作(如序列化、更新、刪除資源等)則放在相應的 Mixin 混入類裡。django-rest-framework 提供了資源操作的 5 個混入類,分別對應資源的建立、查詢、更新、刪除。

  • CreateModelMixin

    提供 create 方法用於建立資源

  • ListModelMixin 和 RetrieveModelMixin

    提供 list 和 retrieve,分別用於獲取資源列表和單個資源

  • UpdateModelMixin

    提供 update 方法用於更新資源

  • DestroyModelMixin

    提供 destroy 方法用於刪除資源

此外,create、list、retrieve、update、destroy 的方法名會被對映為對應的 action,稱為對資源操作的一個動作。前面說到檢視集的一個最大好處就是可以使用路由器(router)自動生成 URL 模式。URL 正是根據 action 的型別來生成的,後面我們會具體說到。

好了,檢視集已經建立完畢,接下來我們從檢視集生成檢視函式,並繫結 URL。

blog/views.py

index = PostViewSet.as_view({'get': 'list'})
blog/urls.py

app_name = "blog"
urlpatterns = [
    # ...
    # path("api/index/", views.IndexPostListAPIView.as_view()),
    path("api/index/", index),
]

等等,不是說檢視集的一個好處是使用路由器自動生成 URL 模式嗎?為什麼還要手工建立檢視函式,然後繫結 URL?

別急,這裡只是演示一下如何從檢視集生成檢視函式並繫結 URL,這樣能夠幫助你更好地理解檢視集的工作方式。事實上,使用路由器自動生成 URL 模式時,路由器內部就是採用了和上面手工生成檢視函式並繫結 URL 一樣的方式。

路由器的使用非常簡單,我們在 初始化 RESTful API 風格的部落格系統 中引入了 DefaultRouter 以開啟 API 互動後臺,DefaultRouter 例項化時預設幫我們註冊了一個 API 互動後臺的根檢視,現在要註冊一個新的檢視,呼叫其 register 方法就可以了:

blogproject/urls.py

from blog.views import PostViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')

Django-rest-framework 提供 SimpleRouterDefaultRouter 兩個路由器類,後者是對前者的擴充,因此通常情況下都使用後者。DefaultRouter 增加了一個 api 的根路由,訪問根路由的 URL 就可以看到其他註冊的全部 api 路由,一會兒我們將會看到具體的效果。

檢視集自動生成 URL 模式非常簡單,只需例項化一個路由器,然後呼叫其 register 方法,這個方法接收 3 個引數,第一個引數是 URL 字首,所有從註冊的檢視集生成的 URL 都會帶有這個字首。第二個引數就是檢視集,第三個引數 basename 用於指定檢視集生成的檢視函式名的字首。在 django 的 URL 中,一條路由通常由 URL 模式,對應的檢視函式和檢視函式名組成。檢視函式名的作用主要用於解析檢視函式所對應的 URL。檢視集最終會被轉為多個檢視函式,那麼這個檢視函式的名字是什麼呢?django-rest-framework 的預設生成規則是 basename-action。

例如這裡 basename='post',列出資源列表的 action 為 list(見上一篇教程中關於 action 的講解),所以生成的獲取文章資源列表的檢視函式名為 post-list,使用 reverse('post-list') 就可以解析出獲取文章資源列表的 API(URL)。

basename 可以不指定,django-rest-framework 會自動從檢視集 get_queryset 方法返回的結果所關聯的 model 獲取一個預設值,其值為 model 名小寫。不過,根據 Python 之禪,顯式優於隱式,因此即使你設定的 basename 和 django-rest-framework 預設生成的一樣,也比不指定要好。

剛才說了,我們使用 DefaultRouter 這個路由器,它會自動幫我們註冊一個根路由,來看看根路由下有什麼。

執行開發伺服器,訪問 http://127.0.0.1:8000/api/,介面如下:

django-rest-framework 為我們自動生成了 API 互動後臺,在這個介面中可以和我們建立的 API 互動,非常方便。API 互動後臺首頁是所有註冊的檢視集對應的 URL。目前只有一條 /api/posts/,點選超連結進去,可以看到 /api/posts/ 的返回結果,即全部文章列表。

但是,目前我們的 api 一股腦將全部文章列表的返回了。但是我們的部落格文章列表是有分頁功能的,接下來我們就使用 django-rest-framework 提供的分頁輔助類,一行程式碼就可以完成分頁功能。


關注公眾號加入交流群

相關文章