エラー:AssertionError: You cannot call `.save()` on a serializer with invalid data.
AssertionError: You cannot call `.save()` on a serializer with invalid data.
上記のエラーが表示された。
エラーが出た状況:
インスタンスを生成することを目的にdjango rest apiでrequests.post()をする。するとdjango アプリケーションで上記のエラーが表示された。
詳しくは、csvファイルをcsv.DictReaderを使ってdict型に変換する。変換したものをjson.dumps()関数を使った戻り値をdataとし、requests.post(URL, data=data)を投げる。しかしながらエラー発生。
具体的なコード
import csv, sqlite3, json, requests INPUT_FILE = "/Users/*****/*****/myjson_test.csv" fieldnames = ("id","title","length","datetime") with open(INPUT_FILE,"r",encoding="utf-8") as f: reader = csv.DictReader(f, fieldnames) for row in reader: headers = {'content-type': 'application/json'} URL = "http://127.0.0.1:8000/api/" res = requests.post(URL, data=json.dumps(row), headers=headers)
エラーの原因を探る
REST apiの設計にエラー原因があるか?
適切にRest APIの設計はできているか。不適切だからエラーが発生しているのではないか?
この問に対して、django rest frameworkの設計によるエラーではないことが判明した。
test = {} test["id"] = "1234567890123" test["title"] = "test_title" headers = {'content-type': 'application/json'} URL = "http://127.0.0.1:8000/api/" res = requests.post(URL, data=json.dumps(test), headers=headers) print(res)
上記のコードが通り、django adminにおいてインスタンスが作成されたのを確認できた。したがって他が原因であることが判明した。
csvファイルをcsv.DictReader()で辞書型にするのに原因があるのか?
上記のデータtestは実際にdict型のデータをjsonのデータに変換している。しかしながらエラーが出たコードはcsvをdict化しているのであってdict型データではない。dict型データでないモノをjson化した際にエラーが発生の原因となってしまう可能性が考えられる。そこで直接的な原因検証ではないがtype()関数を使いデータ型を確認してみた。
test = {} test["id"] = "1234567890123" test["title"] = "test_title" headers = {'content-type': 'application/json'} URL = "http://127.0.0.1:8000/api/" res = requests.post(URL, data=json.dumps(test), headers=headers) print(type(test)) print(type(json.dumps(test))
#結果 <class 'dict'> <class 'str'>
つづいてエラー原因のコードを検討する。
import csv, sqlite3, json, requests INPUT_FILE = "/Users/*****/*****/myjson_test.csv" fieldnames = ("id","title","length","datetime") with open(INPUT_FILE,"r",encoding="utf-8") as f: reader = csv.DictReader(f, fieldnames) for row in reader: headers = {'content-type': 'application/json'} URL = "http://127.0.0.1:8000/api/" res = requests.post(URL, data=json.dumps(row), headers=headers) print(type(row)) print(type(json.dumps(row)))
# 結果 <class 'collections.OrderedDict'> <class 'str'>
やはりdict()関数とcsv.DictReader()関数でデータ型は厳密には異なるようだ。しかしながらjson.dumps()の返り値はstr型なので適切にjson型のデータに変換したと暫定的にみなす。
jsonデータに空の値がある場合どのような挙動になるか?
REST apiにおいて受け渡されるJSONデータのキーに対する値が空である場合、適切にインスタンスが生成されるか検証していなかった。空の値の場合を検証する。
test = {} test["id"] = "1234567890123" test["title"] = "test_title" test["length"] = "" # 空の値を設置 headers = {'content-type': 'application/json'} URL = "http://127.0.0.1:8000/api/" res = requests.post(URL, data=json.dumps(test), headers=headers) print(res)
上記のようにしたところエラーが発生した。
AssertionError: You cannot call `.save()` on a serializer with invalid data.
当初と同じエラーが出てきたので、これが原因だと推定できる。したがって値が空の場合についてどう対処するか考えていく。
jsonデータから空の値のデータは削除してrequests.postしたところインスタンスを生成することができた。対処法はここを参照。しかしながらできないものも出てきた。
結論から言うとmodelsのCharField max_lengthが120に対しそれ以上のものを登録しようとしていた時に同じエラーが起きた。これに対してはmax_lengthをさらに大きくしたところエラーをはかずすべてインスタンスを生成することができた。
まとめ:エラーAssertionError: You cannot call `.save()` on a serializer with invalid data.の原因
まずJSONのキーに対して値が空の場合にこのエラーが出てしまう。
さらに加えてCharFieldのmax_lengthを超えた文字数を登録しようとした場合に同じエラーが出てしまうことが分かった。
追記:その他の原因としてImgaeFieldにファイルパスだけ登録しようと、文字列を渡しても同じようにAssertionError: You cannot call `.save()` on a serializer with invalid data.が出る。