iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0

前言

昨天我們透過客製化 ViewSet 讓他在不同的 action 中回傳不同序列化,達到修改不同傳入傳出的格式。今天前端提了一個需求:「我想要有一個 API 讓我呼叫後如果原本是已完成,就修改成未完成,如果是未完成,就修改成已完成。」

需求

我們來整理一下前端的需求,需要有一個新的 API 呼叫後需要反轉 is_finish 的狀態。

  1. 使用 PATCH 這個方法因為呼叫這個 API 只會修改 is_finish 這個欄位
  2. 新增一個 API 路由 /api/todo/tasks/{task_id}/status(裡面的 task_id 會動態替換表示要修改的任務)

以上就是這次的需求讓我們開始改吧!

修改 ViewSet

讓我們打開 `` 一起把內容修改成下方這個樣子

-from rest_framework import viewsets
+from rest_framework import decorators, response, viewsets

from server.app.todo import models as todo_models
from server.app.todo import serializers as todo_serializers


class TaskViewSet(viewsets.ModelViewSet):
    queryset = todo_models.Task.objects.all()
    serializer_class = todo_serializers.TaskSerializer

    def get_serializer_class(self):
        if self.action == "create":
            return todo_serializers.TaskCreateSerializer

        return super().get_serializer_class()

+   @decorators.action(methods=["patch"], detail=True)
+   def status(self, request, pk):
+       task = self.get_object()
+
+       serializer = self.get_serializer(
+           task,
+           data={"is_finish": not task.is_finish},
+           partial=True,
+       )
+       serializer.is_valid(raise_exception=True)
+       serializer.save()
+
+       return response.Response(serializer.data)


class TagViewSet(viewsets.ModelViewSet):
    queryset = todo_models.Tag.objects.all()
    serializer_class = todo_serializers.TagSerializer

現在這個檔案我們在 TaskViewSet 裡面新增了一個 status 的方法,並幫他裝上 action 裝飾器,告訴 ViewSet 說這是一個新增的路由,url 的名稱再不特別指定的情況下會使用方法的名稱,這邊會是 status 所以等等路由會變成 /api/todo/tasks/{task_id}/status,那 ViewSet 怎麼知道要幫我們加上 task_id 呢?是因為我們在 action 裝飾器中有加上 detail=True 的設定,告訴他我們需要在 url 中指定 id 才能呼叫到這個路由。

而 status 方法的參數,第一個是 self 這個是 Python 的方法都需要收的參數代表物件本身,接著是 request 是 view 規定要收的參數,最後的 pk 是因為我們設定 detail=True 所以需要收的參數,代表使用者在 url 中指定的 ID。

另一個需求是讓這個路由需要使用 patch 方法呼叫,我們是在 action 裝飾器中設定 methods=["patch"] 所以 ViewSet 才會知道使用者必須使用 PATCH 方法呼叫。

接著讓我們看看這個方法中到底做了什麼處理,首先是呼叫了 get_object 這個 ViewSet 提供的方法,他會根據使用者在 url 中所提供的 id 到資料庫中將對應的物件撈取出來,讓我們可以獲得使用者指定的任務。

再來是 get_serializer 這個一樣是 ViewSet 提供的方法,讓我們獲取對的序列化(他會根據 get_serializer_class 回傳的 class 決定你會拿到哪個)並且將 task 放入其中,代表的是要更新這個任務,同時提供 data 指定要更新的欄位以及值,這邊更新的是 is_finish 欄位並是反轉現有的值,最後傳入的 partial 意思是告訴序列化,我們這次的更新只會傳入部分的欄位,避免序列化誤以為我們少傳入欄位而報錯。

完成序列化的初始化後,我們呼叫了 is_valid 來判斷序列化是否有錯誤(raise_exception 是告訴序列化如果有錯誤產生的話直接引法 exception),如果沒有錯誤就呼叫 save 將資料存入資料庫。

方法的錯後將序列化的資料回傳給使用者,這樣就完成了!

現在你可以打開 Postman 用 PATCH 方法呼叫 http://127.0.0.1:8000/api/todo/tasks/{task_id}/status(記得替換 {task_id} 不用傳入任何 HTTP Body 就可以看到 is_finish 不斷變動了。

總結

今天我們在 ViewSet 中額外增加了一個路徑了。結束前別忘了檢查一下今天的程式碼有沒有問題,並排版好喔。

ruff check --fix .
black .
pyright .

明天讓我們來講講權限相關的東西吧!要開始為 API 加上驗證與權限了,不然大家都可以亂使用我們的 API 了。

P.S. 今天的檔案更新可以參考我的 Git Commit 大家可以搭配服用


上一篇
Day11 - 客製化 ViewSet 使用不同的格式
下一篇
Day13 - API 權限控管
系列文
Django REST 大冒險:探索精彩紛呈的 API 開發世界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言