Let's Django!Let's Django!

這是 Django Tutorial 的第 9 篇、DRF 系列的第 3 篇。

範例程式碼可參考我的 GitHub 專案,更多教學請見「Django 文章總覽」。

本文相關的程式碼改動,都集中在這個 PR


Views 是 Django 中處理 HTTP 請求的核心邏輯。它的作用是接收請求、處理請求,最後返回 response:

A view function, or view for short, is a Python function that takes a web request and returns a web response.

在 Django 中,我們可以使用兩種方式來撰寫 views:

  1. Function-Based Views(FBV),其實就是一個 Python 函式,也是 Django 預設的 view 寫法,所以又稱 view 函式
  2. Class-Based Views(CBV)。CBV 是 Django 1.3 版本引入的,它是基於類別的 view 寫法。

CBV 與 FBV

CBV vs. FBV」是初學 Django 一個常見的議題。如系列的第一篇所言:

CBV 有著重用程式碼優勢,適合大型專案。而 FBV 則以簡單、直接為賣點,方便快速開發中小型專案。

究竟要選哪個,取決於個人喜好與專案需求。

Django REST framework(以下簡稱 DRF)同時支援這兩種 views。

事實上,DRF 顯然更加鼓勵使用 CBV,不僅提供了許多現成的 generic views,它的很多元件也是基於 CBV 設計的。

不過,本文只打算介紹 DRF 中的 FBV,並實作一個 DRF view function。


Django app 中的 views.py

回到我們的範例專案——Django Tutorial 中的postapp。

Django Tutorial 是一個簡單的部落格專案,與我的「系列:Django ORM 外鍵教學共用世界觀。

可以了解一下專案模型介紹以及範例程式碼模型調整

不過因為它們的設計非常簡單,直接看程式碼應該也無妨。

views.py

views.py是 Django app 中處理 HTTP 請求的地方,我們在這裡定義各種 views。

上一回我們已經在 views.py 中用 view 函式實作了一個簡單的 API:

1
2
3
4
from django.http import JsonResponse

def hello_world(request):
return JsonResponse({'message': 'Hello, world!'})

這次我們要再進一步,實作一個更複雜的 API,且更多使用 DRF 的功能。


DRF View Function

首先,我們需要引入api_view裝飾器與Response類別:

1
2
3
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

還有最後的status模組,用於設定 HTTP 狀態碼。

接著,我們將這些 DRF 元件套用在我們的 view function 上:

1
2
3
4
5
@api_view(['GET'])
def get_posts(request):
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

這樣我們就實作了一個簡單的 DRF GET API,用於取得所有的文章。

這裡的PostSerializer是一個序列化器,我們會在下一篇文章中介紹。

此刻,我們先專注於api_view裝飾器與Response類別上。


如前所述,Django 的 view 本質上是一個 Python 函式,它接收一個 HTTP request,返回一個 HTTP response。

那 DRF 的 view function 又有什麼不同呢?——其實就是整合了 DRF 提供的功能。

我們來看看 DRF 究竟幫我們做了什麼,讓開發 API 變得更簡單。

一、api_view 裝飾器

api_view裝飾器,它的主要目的是將普通的 Django view 轉換為 DRF 的 API view。為此,它做了以下幾件事:

  1. 方法限制@api_view(['GET'])指定了這個 view 允許的 HTTP 方法。當請求方法不在允許列表中時,DRF 會自動回應405 Method Not Allowed
  2. request:DRF 將 Django 的HttpRequest物件轉換為Request物件。它更加方便操作,比如可以通過request.data取得 POST 請求的資料,而不需要自己去處理request.POSTrequest.body
  3. 例外處理api_view重新封裝了 view,提供了內建的錯誤處理機制。

其中第二點是最重要的,因為 DRF 的Request物件提供了更多的功能,包括實務中大量使用的request.data

Django 原生的HttpRequest物件,只提供了request.POSTrequest.body,兩者在使用上都有一定限制

相關文章:Django HttpRequest 常用屬性介紹

而 DRF 對其進行了簡化,讓我們可以更加專注在 API 的邏輯上,這確實是一大加分。

二、Response 類別

Response是 DRF 提供的自定義 response,它繼承自 Django 的HttpResponse,並提供了更多的功能。

不過在我看來,Response的重要性沒有api_view裝飾器那麼高,使用 Django 的JsonResponse也能達到類似的效果:

  1. 資料轉換Response 能夠接受 Python 字典、list 或其他可序列化的資料結構,並自動將它們轉換為 JSON 回應。你不用手動調用 json.dumps
  2. 狀態碼:你可以通過 Response(data, status=status.HTTP_200_OK) 設置回應的 HTTP 狀態碼。

通常,我們主要的需求就是返回一個 JSON response。

所以這裡就不多討論Response,很多時候我都是直接使用JsonResponse

三、status 模組

DRF 的status模組提供了一系列的 HTTP 狀態碼,將它們封裝成了常數。比如:

  • status.HTTP_200_OK:200 OK
  • status.HTTP_201_CREATED:201 Created
  • status.HTTP_400_BAD_REQUEST:400 Bad Request
  • status.HTTP_404_NOT_FOUND:404 Not Found
  • status.HTTP_405_METHOD_NOT_ALLOWED:405 Method Not Allowed

這對於提高程式碼的可讀性與一致性很有幫助,這樣我們就不用牢牢記住每個狀態碼的數字與背後的意義——尤其是那些比較少用到的狀態碼。


目前為止,新的 API view function 的框架已經完成了,可惜它還無法運作——因為我們還沒有定義PostSerializer

這部分我們下回再來處理。文章結束前,我們還要對「路由」進行一些調整。

路由設定

post/urls.py新增一個路由,並調整import方式:

1
2
3
4
5
6
7
8
from django.urls import path

from post import views

urlpatterns = [
path('hello/', views.hello_world),
path('posts/', views.get_posts), # 新增這一行
]

如果要透過 API 取得所有文章,就要訪問/post/posts/這個 path。

path 的前半部是專案的一級路由,它們是由DjangoTutorial/urls.py設定的:

1
2
3
4
urlpatterns = [
path('admin/', admin.site.urls),
path('post/', include('post.urls')),
]

後半部是 Django app 的路由。這樣的設計有助於管理與維護。

不過/post/posts/這 path 的設計看起來有點怪——語意上有所重複。這主要是目前的情境太過簡單造成的。所以我們先取消一級路由的前綴,改為:

1
2
3
4
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('post.urls')), # 將前綴去掉
]

讓取得所有文章的 path 變成/posts/,看起來更加自然。