DRF Response 與 Django JsonResponse:那些我們「習慣性」使用的背後原理

大多數 Django 開發者可能都跟我一樣,在 [[Django]] 專案中,幾乎 99% 的情況下都會搭配使用 DRF ([[Django REST Framework]]) 套件。

因此,當我向伺服器返回回應時,無論該回應是 API 回應,還是模板發出的簡單 JSON 請求,我都會不假思索地使用 Response 這個返回結構化回應的類別。真的是完全不經思考地使用。

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

然而,今天我突然有個疑問:「話說回來,Responsedjango.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 則可以將複雜的模型實例或查詢集放入序列化器這個「自動烹飪機」中,然後直接傳遞其結果即可。因為渲染器會自動將其精美地轉換成最終的 JSON 字串。

有時與像 ChatGPT 這樣「過於細心」的 AI 協作時,我曾幾次看到它過度熱心或過度防禦地逐一建立回應資料,最終再呼叫 Response 來返回回應。

對於重視效率的開發者來說,看到這樣的程式碼可能會感到有些不適。從現在開始,如果 AI 產生了這樣的程式碼,我們就果斷地修改它吧。


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

這是 100% 基於我個人主觀經驗得出的結論,但至少我的回答是:「完全沒問題,甚至值得推薦。

當然,從技術角度來看,Response 會經過內容協商過程並檢查多個渲染器,因此可能會比 JsonResponse 產生微乎其微的額外運算。但在現代基礎設施(即使在我運行的 Raspberry Pi 5 上!)中,這種差異幾乎難以察覺。

相反地,持續使用 Response 所帶來的好處要大得多。

  1. 一致性: 可以在整個專案中統一回應格式。
  2. 偵錯: 透過瀏覽器存取時,可以方便地透過 Browsable API 直接查看資料。
  3. 靈活性: 未來需要將回應格式擴展為 XML 或 YAML 時,無需修改程式碼,只需調整設定即可應對。

總結:了解差異後,心情豁然開朗

透過今天的學習,我得出的結論是:

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

然而,當我了解了自己每天使用的工具內部運作原理,以及為什麼非得使用 Response 而不是 JsonResponse 這個獨立類別的原因後,我感到非常暢快。果然,開發者只有在不斷追問「為什麼?」的時候,才能更進一步成長。

好,既然已經了解了它的本質,我應該更有效地、更放心地使用 Response 了。


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

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