エラー概要
DjangoでTypeError Object of type 'bytes' is not JSON serializable
が表示されました。本記事ではそのエラー対応について記載します。
前提
以下のコードがあるとします。
def filter_company_rank(request):
query_set = CompanyRank.objects.all()
# フリーキーワードで検索する
# 省略
return get_list_data_table(query_set)
def get_list_data_table(query_set):
data = query_set.select_related(
'company_rank_id'
).values(
'company_rank',
'update_date',
'update_user_id__username',
)
return JsonResponse(json.dumps(data))
def get_all_data(request):
result = filter_company_rank(request)
return result
上記のコードでは、フリーキーワードでユーザーが入力したキーワードがDBにあるかどうか検索して表示する関数を書いています。
ここで、return JsonResponse(json.dumps(data))
のままだと上述のエラーが発生しました。
原因
原因は、QuerySetオブジェクトがデフォルトのJSONシリアライザによって直接シリアライズできなかったためです。
QuerySetとは、データベースからのクエリ結果を表すための特殊なオブジェクトで、以下のようなオブジェクトを指します。
<QuerySet [<CompanyRank: CompanyRank object (1)>]>
JSONシリアライザは通常、プリミティブなデータ型(整数、文字列、リストなど)や辞書形式のオブジェクトに対しては動作しますが、QuerySetオブジェクトはそのままではシリアライズできません。詳細は公式ドキュメントを参照ください。
json --- JSON エンコーダおよびデコーダ
また、json.dumps()
を使用した場合、data
オブジェクトをJSON形式の文字列に変換することはできますが、そのままではシリアライズされたオブジェクトとして扱われません(ここがポイント)。
シリアライズとは
データを直列化してバイト列やテキストなどの形式に変換すること。
Pythonのjson
モジュールを使用してオブジェクトをJSON形式にシリアライズする場合、シリアライズ可能なオブジェクトはJSONに変換できます。ただし、シリアライズ不可能なオブジェクトはエラーが発生します。
反対に、JSON形式のデータをPythonのオブジェクトや文字列に変換することをデシリアライズと言います。
解決策
QuerySetオブジェクトをリストに変換してからJSONレスポンスに渡すことで、シリアライズ可能なデータとして扱うことができます。
return JsonResponse(json.dumps(data))
を
return JsonResponse(list(data), safe=False)
にすると正常にレスポンスが返ってきます。
ちなみに、以下でも成功しました。
return HttpResponse(json.dumps(list(data)), content_type='application/json')
ただし、json.dumps()
の結果を文字列として直接返しています。また、JsonResponse
は Django のビルトインのレスポンスクラスであり、自動的に正しいContent-Type
ヘッダーを設定してくれますし、簡潔に記載できるので、JsonResponse
の使用の方が推奨されると思います。
備忘録
non-dict objects to be serialized
が出た場合
以下の場合In order to allow non-dict objects to be serialized set the safe parameter to False.
というエラーが発生します。
return JsonResponse(json.dumps(list(data)))
これは、JsonResponse
がデフォルトでは辞書型のオブジェクトを受け取り、それをJSON形式にシリアライズしてレスポンスとして返すためです。safe
パラメータをFalse
に設定することで、辞書以外のオブジェクトもシリアライズすることができるようになります。これにより、リストやクエリセットなどの辞書以外のオブジェクトも正常にシリアライズされます。
文字列をシリアライズできない他の例
以下は文字列をシリアライズできない例です。
冒頭と同じエラーがでます。
return JsonResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data), content_type='application/json')
return HttpResponse(json.dumps(data))