## DRF Response 对比 Django JsonResponse:那些我们“随手”使用的究竟是什么? {#sec-415c48e5bc9d} 作为一名 Django 开发者,我想大多数人可能都和我一样,在几乎 99% 的 [[Django]] 项目中都会同时使用 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`,它就会显示我们熟悉的漂亮“可浏览 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` 则可以将复杂的模型实例或查询集放入序列化器这个“自动化烹饪机”中,然后直接抛出其结果即可。因为渲染器 (Renderer) 会自动将其漂亮地转换成最终的 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/) 注册账号后进行关注。在博客上写作并有人阅读您的文章的体验……相当有趣。