iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0

前言

昨天我們學會怎麼做分頁了,今天我們要來學習怎麼在 API 實作搜尋與排序,方便使用者可以找到他想找到的資料。

排序

首先我們來做排序功能,讓我們打開 server/app/todo/views.py 並依照下方說明修改

-from rest_framework import decorators, response, viewsets
+from rest_framework import decorators, filters, 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.order_by("id")
    serializer_class = todo_serializers.TaskSerializer
+   filter_backends = (filters.OrderingFilter,)
+   ordering_fields = ("id", "title")

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

        return super().get_serializer_class()

# ...... 以下省略 ......

上面的代表說我們在這個 ViewSet 設定一個 Filter 是 OrderingFilter 讓使用者可以排序,並設定可以排序的欄位為 id 以及 title

這時候大家是著使用 GET 請求下方的網址(要記得啟動虛擬環境 & 伺服器唷)

因為我們有設定可排序欄位為 id 以及 title 所以可以將上方 url 參數換成 title

同時他也支援多個欄位排序例如 http://127.0.0.1:8000/api/todo/tasks?ordering=title,-id 代表先依照 title 排序(由小到大)再依照 id 排序(由大到小)

現在我們一樣是在單一 ViewSet 中設定 Filter,現在讓我們把它移到全域去

打開 server/app/todo/views.py 並依照下方修改

-from rest_framework import decorators, filters, response, 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.order_by("id")
    serializer_class = todo_serializers.TaskSerializer
-   filter_backends = (filters.OrderingFilter,)
    ordering_fields = ("id", "title")

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

        return super().get_serializer_class()

# ...... 以下省略 ......

接著打開 server/settings.py 並修改

# ...... 以上省略 ......

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
+   "DEFAULT_FILTER_BACKENDS": [
+       "rest_framework.filters.OrderingFilter",
+   ],
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
    "DEFAULT_PAGINATION_CLASS": "server.utils.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
}

# ...... 以下省略 .......

現在可以再次請求看看剛剛的測試,應該會發現功能應該都一樣。接著我們來在其他 ViewSet 也加上排序的設定,讓我們打開 server/app/todo/views.py 並修改

# ...... 以上省略 ......

class TagViewSet(viewsets.ModelViewSet):
    queryset = todo_models.Tag.objects.order_by("id")
    serializer_class = todo_serializers.TagSerializer
+   ordering_fields = ("id", "name")

接著大家可以試看看 Tag 列表的 API 應該也具有排序功能囉~

搜尋

接著我們來做搜尋功能讓我們打開 server/settings.py

P.S. 這邊我就直接在全域設定 Filter 單獨的設定方法與前面說的相同

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
    "DEFAULT_FILTER_BACKENDS": [
        "rest_framework.filters.OrderingFilter",
+       "rest_framework.filters.SearchFilter",
    ],
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
    "DEFAULT_PAGINATION_CLASS": "server.utils.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
}

上方的改動是設定全部的 ViewSet 除了排序的 Filter 以外需要額外加上 Search 的 Filter

接著打開 server/app/todo/views.py 並修改

# ...... 以上省略 ......

class TaskViewSet(viewsets.ModelViewSet):
    queryset = todo_models.Task.objects.order_by("id")
    serializer_class = todo_serializers.TaskSerializer
    ordering_fields = ("id", "title")
+   search_fields = ("title", "description")

# ...... 以下省略 ......

這邊的設定是當使用者搜尋關鍵字時要在這兩個欄位尋找(是模糊搜尋其中一個欄位出現關鍵字即符合)

現在是著用 GET 方法請求 http://127.0.0.1:8000/api/todo/tasks?search=關鍵字 就可以搜尋標題或說明有出現關鍵字的任務。

現在我們來讓 Tag 相關的 API 也支援搜尋,讓我們打開 server/app/todo/views.py 並修改

# ...... 以上省略 ......

class TagViewSet(viewsets.ModelViewSet):
    queryset = todo_models.Tag.objects.order_by("id")
    serializer_class = todo_serializers.TagSerializer
    ordering_fields = ("id", "name")
+   search_fields = ("name",)

現在我們的 Tag API 也支援搜尋名稱了。

總結

今天我們學會了如何做搜尋與排序。

結束前別忘了檢查一下今天的程式碼有沒有問題,並排版好喔。

ruff check --fix .
black .
pyright .

今天的內容就到這邊了,明天我們會學習如何做篩選,一起期待一下吧。

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


上一篇
Day17 - 為 API 加上分頁
下一篇
Day19 - API 的篩選(Filter)與 ORM 搜尋語法
系列文
Django REST 大冒險:探索精彩紛呈的 API 開發世界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言