diadia

興味があることをやってみる。自分のメモを残しておきます。

Django Rest FrameworkにRetrofitで送信する(multipart編)

やりたいこと

django rest frameworkをバックエンドにAndroid端末からデータを送信する。その結果、特定のModelのインスタンスを生成する。

条件:

  1. 特定のModelは、ImageFieldを含むクラスである。
  2. 特定のModelインスタンスを生成するためにmultipart通信を行い、画像とテキストデータを送信する。
  3. Android端末からデータ送信を行う手段としてretrofitを使用する。

まずそもそもdjangoの受信データ型は?

djangoにおいてhttp通信で受け取ったデータは、request.POSTである。django rest frameworkを使ったAPIViewにおけるデータはrequest.dataである。 request.POST, request.dataはともにdict型に似たデータ型である。 つまり、djangoは外部から受信したデータはdictチックなデータとして受け入れるという前提がある。

Serializerの引数のデータ型は?

serializerの引数は場合によって異なる。クライアントにJSON型でレスポンスする場合には、django内で定めたmodels.Modelのサブクラスインスタンスを引数とする。クライアントから受け取ったデータを利用して新たにインスタンスを生成したい場合には、引数はrequest.dataである。

インスタンス生成の見通し

multipart通信は画像データとString型(JSON)を混在して送信する。 したがってdjangoにおけるrequest.dataは、画像データとstr型データ(JSON)を含んだdictチックなデータとなる。 このデータからJSONを取り出してserializerにデータを渡す。 するとImageFieldを除いたModelインスタンスを生成できるのは想像できる。 そしてそのインスタンスに画像データを追加すればimageFieldを持ったインスタンスを生成する事ができる。

retrofitでmultipart通信を行うと何が他に必要か?

まずdjango側では、request.dataからJSONデータを取り出すとそのデータ型はstr型である。これをdict型に変換しないとserializerによるインスタンスを生成できない。

import json

#request.dataからjsonデータを取り出し
strJsonData = request.data["jsonData"] 

dictJsonData = json.loads(strJsonData)

次にAndroid側では、retrofitで通常送信する場合には引数としてあるモデルオブジェクトを送信する事ができる。 この送信結果としてあるモデルの属性と属性値というJSON形式で送信することができる。

一方、retrofitのmultipart通信の場合には、引数としてあるモデルオブジェクトを取ることができない。テキストデータ(String型)であれば、RequestBodyオブジェクトが引数である。このRequestBodyインスタンスを生成する際にJSON形式で、あるモデルオブジェクトのデータを使う必要がある。 そういうわけで以下のコードが追加で必要になる。

val jsonData = Gson().toJson(MyModelObj)

val reqBody = RequestBody.create(MediaType.parse(application/json), jsonData)

関連記事

retrofit multipart送信を行う - diadia