DRF Response 对比 Django JsonResponse:那些我们“随手”使用的究竟是什么?

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