DRF 的 Response 與 Django 的 JsonResponse:「隨手用」的背後真相

與大多數 Django 開發者一樣,我在 [[Django]] 專案中幾乎 99% 都會搭配使用 DRF ([[Django REST Framework]]) 套件。因此,當向伺服器返回回應時,無論該回應是 API 回應,還是模板中請求的簡單 JSON 請求,我都會不假思索地使用 Response 這個結構化的回應類別。我真的就是「隨手用」而已。

因為它不僅與序列化器(Serializer)配合得天衣無縫,而且在所有回應中預設使用 Response 類別,就無需再費心思考。

然而,今天我突然想到:「話說回來,Response 到底與 django.http.JsonResponse 有什麼不同?差別又在哪裡呢?」因此,今天我將嘗試解答這個疑問。


1. 根源不同:靜態 (Static) 還是彈性 (Flexible)?

JsonResponse和Response的差異圖示

首先,我查閱了這兩者的「血統」,發現了一個有趣的差異。

  • JsonResponse: 繼承自 Django 的 HttpResponse。它的目的非常明確:「接收數據,然後透過 json.dumps() 進行序列化,再將 Content-Type 設定為 application/json 並發送出去。」就這樣,一個非常簡單直觀的類別。

  • DRF Response: 它繼承自 SimpleTemplateResponse。嗯?這是什麼意思?為什麼會繼承與模板相關的類別呢?從這裡開始,Response 的真實身份逐漸浮現。原來我一直隨手使用的這個類別,是一個在最終結果尚未確定之前的「渲染前數據容器」


2. 核心差異:內容協商 (Content Negotiation)

JsonResponse 就像是個「非 JSON 不可」的固執傢伙,而 DRF 的 Response 則非常靈活且聰明。

DRF 的 Response 會詢問客戶端想要什麼。(這就是所謂的 Content Negotiation,內容協商。) 如果客戶端在標頭中發送 Accept: text/html,它會顯示我們熟悉的漂亮「Browsable API」介面;如果發送 Accept: application/json,則只會發送純粹的 JSON 數據。

也就是說,Response 類別本身僅用於承載數據,而實際以何種形式呈現則由 DRF 的 Renderer 決定。我們之所以能夠隨心所欲地使用 Response,並獲得符合情境的回應,正是因為 DRF 在背後努力進行了協商。

這真是一個令人驚訝的發現與領悟。我不禁感嘆,這工具設計得真是精密。每當遇到這樣的時刻,我都會對 Django/DRF 的開發者和貢獻者們心懷數秒的感激。


3. 序列化 (Serialization) 的便利性

當使用 JsonResponse 時,我們需要手動將數據「整理」成 Python 字典或列表的形式才能傳遞。相比之下,Response 與 DRF 的 Serializer 簡直是天作之合。

讓我們來看一個簡單的例子。如果需要返回部落格文章資訊,會是怎樣呢?

案例 A: JsonResponse (手動處理版本)

from django.http import JsonResponse
def post_detail(request, pk):
    post = Post.objects.get(pk=pk)
    # 必須手動將數據一個個建立成字典。
    # 如果有 20 個欄位呢?嗯... 可能會有點惱火。
    data = {
        "title": post.title,
        "content": post.content,
        "created_at": post.created_at.strftime("%Y-%m-%d"), # 日期格式也要手動處理!
    }
    return JsonResponse(data)

案例 B: DRF Response (自動化版本)

from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view(['GET'])
def post_detail(request, pk):
    post = Post.objects.get(pk=pk)
    serializer = PostSerializer(post)

    # 直接丟出 .data 即可。
    # 複雜的關聯(Foreign Key)或日期格式化都由序列化器自動處理。
    return Response(serializer.data)

看到了嗎?JsonResponse 需要我們親自處理食材並擺盤,而 Response 則可以將複雜的模型實例或查詢集放入 Serializer 這個「自動烹飪機」中,然後直接丟出結果。因為 Renderer 會自動將其轉換為漂亮的 JSON 字串。

有時在與像 ChatGPT 這樣一絲不苟的 AI 合作時,我曾多次遇到它出於過度熱心或過度防禦,而逐一建立回應數據,最終才呼叫 Response 返回的情況。注重效率的開發者看到這樣的程式碼可能會感到有些不適。現在,如果 AI 產生了這樣的程式碼,我們就該果斷地修正它。


那麼,即使是簡單的 JSON 回應,也應該繼續使用 Response 嗎?

這是我 100% 基於個人主觀經驗的結論,至少我的回答是:「完全沒問題。甚至建議這麼做。

當然,從技術上來說,Response 會經過內容協商 (Content Negotiation) 過程並檢查多個 Renderer,因此可能會比 JsonResponse 多出非常微小的運算量。然而,在現代基礎設施(即使是我在 Raspberry Pi 5 上運行!)中,這種差異幾乎難以察覺。

相反地,持續使用 Response 所帶來的好處遠遠更大。

  1. 一致性: 可以在整個專案中統一回應格式。
  2. 除錯: 透過瀏覽器存取時,可以方便地透過 Browsable API 直接查看數據。
  3. 彈性: 未來如果需要將回應格式擴展為 XML 或 YAML,無需修改程式碼,只需透過配置即可應對。

結語:了解差異後,豁然開朗

透過今天的學習,我得出了這樣的結論:

「歸根結底,既然沒有顯著的性能差異,在 DRF 環境下,繼續沿用 Response 對於心理健康更有益處。」

然而,當我了解了每天使用的工具內部是如何運作的,以及為什麼必須使用 Response 而非 JsonResponse 這個獨立的類別時,感覺真是豁然開朗。果然,開發者只有不斷追問「為什麼?」,才能更進一步成長。

現在,既然已經了解了它的本質,我就能更自信、更有效地使用 Response 了。


如果有人也曾像我一樣,因為 Django 和 DRF 之間的細微差異而輾轉難眠,希望這篇文章能對您有所幫助。 如有任何疑問或意見,歡迎在下方留言!

如果這篇文章對您有幫助,請點擊追蹤!您可以在 Mikihands Blog Flatform 註冊帳號後進行追蹤。在部落格撰寫文章並有人閱讀的體驗... 相當有趣。