## DRF Response 與 Django JsonResponse:那些我們「習慣性」使用的背後原理 {#sec-415c48e5bc9d} 大多數 Django 開發者可能都跟我一樣,在 [[Django]] 專案中,幾乎 99% 的情況下都會搭配使用 DRF ([[Django REST Framework]]) 套件。 因此,當我向伺服器返回回應時,無論該回應是 API 回應,還是模板發出的簡單 JSON 請求,我都會不假思索地使用 `Response` 這個返回結構化回應的類別。真的是完全不經思考地使用。 因為它不僅與序列化器(Serializer)配合得天衣無縫,而且在所有回應中預設使用這個 `Response` 類別,就完全不需要再費心思考了。 然而,今天我突然有個疑問:「話說回來,`Response` 和 `django.http.JsonResponse` 到底哪裡不同?差別又有多大?」所以,今天我打算來解開這個疑惑。 --- ## **1. 天生不同:靜態(Static)還是彈性(Flexible)?** {#sec-2451e29dd2a3} ![JsonResponse和Response的差異圖示](/media/whitedec/blog_img/66a44f9db4b44419a1a34d72608f8eb9.webp) 首先,當我研究這兩個傢伙的「家譜」時,發現了一些有趣的差異。 * **JsonResponse:** 它繼承自 Django 的 `HttpResponse`。目的非常明確:「接收資料,然後使用 `json.dumps()` 進行序列化,再將 Content-Type 設定為 `application/json` 後發送出去。」就這樣。這是一個非常簡單直觀的傢伙。 * **DRF Response:** 它繼承自 `SimpleTemplateResponse`。嗯?這是什麼?為什麼會繼承與模板相關的類別呢?從這裡開始,`Response` 的真實面貌就浮現了。我一直不假思索地使用的這個傢伙,原來是一個尚未預先決定最終呈現形式的「**渲染前資料容器**」! --- ## **2. 核心差異:內容協商 (Content Negotiation)** {#sec-4b3b34038277} `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) 的便利性** {#sec-f361c5e0b07e} 使用 `JsonResponse` 時,我們必須手動將資料「整理」成 Python 字典或列表的形式才能傳遞。相對地,`Response` 與 DRF 的 Serializer 簡直是天作之合。 讓我們來看一個簡單的例子。如果要返回部落格文章資訊,情況會如何呢? **案例 A: JsonResponse (手動處理版本)** ```python 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 (自動化版本)** ```python 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 嗎?** {#sec-ff7e587ad4e3} 這是 100% 基於我個人主觀經驗得出的結論,但至少我的回答是:「**完全沒問題,甚至值得推薦。**」 當然,從技術角度來看,`Response` 會經過內容協商過程並檢查多個渲染器,因此可能會比 `JsonResponse` 產生微乎其微的額外運算。但在現代基礎設施(即使在我運行的 Raspberry Pi 5 上!)中,這種差異幾乎難以察覺。 相反地,持續使用 `Response` 所帶來的好處要大得多。 1. **一致性:** 可以在整個專案中統一回應格式。 2. **偵錯:** 透過瀏覽器存取時,可以方便地透過 Browsable API 直接查看資料。 3. **靈活性:** 未來需要將回應格式擴展為 XML 或 YAML 時,無需修改程式碼,只需調整設定即可應對。 --- ## **總結:了解差異後,心情豁然開朗** {#sec-b1ff40748b2c} 透過今天的學習,我得出的結論是: > **「歸根結底,兩者之間並沒有顯著的性能差異,在 DRF 環境下,繼續沿用 Response 對於心理健康更有益處。」** 然而,當我了解了自己每天使用的工具內部運作原理,以及為什麼非得使用 `Response` 而不是 `JsonResponse` 這個獨立類別的原因後,我感到非常暢快。果然,開發者只有在不斷追問「為什麼?」的時候,才能更進一步成長。 好,既然已經了解了它的本質,我應該更有效地、更放心地使用 `Response` 了。 --- 如果有像我一樣因為 Django 和 DRF 之間微妙差異而輾轉難眠的朋友,希望這篇文章能對您有所幫助。 如有任何疑問或意見,歡迎在評論區留言! 如果這篇文章對您有幫助,請點擊追蹤!您可以在 [Mikihands Blog Flatform](https://blog.mikihands.com/) 註冊帳號後進行追蹤。在部落格上撰寫文章並有人閱讀的體驗... 相當有趣。