DRFのResponse vs DjangoのJsonResponse:「なんとなく」使っていたものの正体

多くのDjango開発者と同様に、私も[[Django]]プロジェクトではほぼ99%のケースでDRF ([[Django REST Framework]])パッケージを併用しています。

そのため、サーバーに応答を返す際、それがAPI応答であれ、テンプレートからリクエストされた単純なJSON要求であれ、特に気にせず、構造化された応答クラスであるResponseを使用しています。本当に何も考えずに使っていました。

なぜなら、シリアライザーとの相性が非常に良いだけでなく、すべての応答にこの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と相性抜群です。

簡単な例を見てみましょう。もしブログの投稿情報を返す必要があるとしたらどうでしょうか?

Case 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)

Case 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はContent Negotiationの過程を経て複数のレンダラーをチェックするため、JsonResponseよりもごくわずかに演算が多くなる可能性があります。しかし、その差は現代のインフラ(私が動かしているRaspberry Pi 5でさえ!)では体感できないレベルです。

むしろResponseを使い続けることで得られるメリットの方がはるかに大きいです。

  1. 一貫性: プロジェクト全体で応答形式を統一できます。
  2. デバッグ: ブラウザでアクセスした際にBrowsable APIを通じてデータをすぐに確認しやすく便利です。
  3. 柔軟性: 後で応答フォーマットをXMLやYAMLに拡張する必要が生じた場合でも、コードの修正なしに設定だけで対応可能です。

終わりに:違いを知ってスッキリ!

今日の学習を通じて得た結論はこうです。

「結局、大きな性能差があるわけでもなく、DRF環境であれば今まで通りResponseを使うのが精神衛生上良い。」

しかし、私が毎日使っていたツールが内部的にどのように動作しているのか、なぜわざわざJsonResponseではなくResponseという別のクラスを使う必要があったのか、その理由を知ってとても気分が爽快です。やはり開発者は「なぜ?」という問いを止めないときに、一歩成長するものだと感じます。

さあ、正体も分かったので、これからはもっと気持ちよくResponseを使いこなしていこうと思います。


もし私のようにDjangoとDRFの微妙な違いのせいで夜も眠れなかった方がいらっしゃれば、この記事が助けになったことを願います。 ご質問やご意見があればコメントで残してください!

この記事が役立ったと思ったら、ぜひフォローしてください!フォローはMikihands Blog Flatformに登録してアカウントを作成すると可能です。ブログに記事を書いて誰かに読んでもらう経験は…なかなか面白いですよ。