FormとSchemaの違いvalidation編
FormとSchemaの違いvalidation編
FormとSchemaの違いをプレゼンテーション側からみた話を前に書来ましたが validation側から見た場合にも両者は違うようです。
エラー時にSchemaは元の入力値を捨てても良いが、Formは元の入力値にアクセスできることが重要
Schemaライブラリの場合
Schemaライブラリでのvalidationでエラーが発生した時のことを考えましょう。例えばユースケースとしてJSONAPIなどがあげられると思います。 以下のようなschemaがあった場合には、対応したフィールドのエラーメッセージが返ってくれば十分でしょう。
from datetime import date from marshmallow import Schema, fields, ValidationError class DateTriple(Schema): year = fields.Int() month = fields.Int() day = fields.Int() def make_object(self, data): try: return date(**data) except: return None @DateTriple.validator def datecheck(self, data): try: date(**data) except: raise ValidationError("invalid paramaters: {}".format(data)) class FileSchema(Schema): name = fields.String() ctime = fields.Nested(DateTriple) input_data = { "name": "foo.txt", "ctime": { "year": "2000", "month": "@@", "day": "99" } } schema = FileSchema() data, errors = schema.load(input_data) # {'name': 'foo.txt', 'ctime': None} # { # 'ctime': { # 'month': ["invalid literal for int() with base 10: '@@'"], # '_schema': ["invalid paramaters: OrderedDict([('year', 2000), ('month', None), ('day', 99)])"] # } # }
Formライブラリの場合
Formライブラリの場合は少し状況が違います。例えばFormライブラリを使うケースとしてはajax無しのシンプルな投稿フォームを考えてみます。 この場合入力値を間違えた場合に一度入力した箇所の入力値がリセットされるというフォームは使いにくいのではないでしょうか? また、勝手に各フォームのフィールドの型に対応したデフォルト値に置き換わっていても嫌な気持ちになるでしょう。 したがって、Formライブラリでは受け取ったPOSTデータの値を復元して返す必要がありそうです。
from datetime import date import marshmallow_form as mf class DateTriple(mf.Form): year = mf.Int() month = mf.Int() day = mf.Int() def make_object(self, data): try: return date(**data) except: return None @mf.Form.validator def datecheck(self, data): try: date(**data) except: raise ValidationError("invalid paramaters: {}".format(data)) class FileForm(mf.Form): name = mf.String() ctime = mf.Nested(DateTriple) input_data = { "name": "foo.txt", "ctime.year": "2000", "ctime.month": "@@", "ctime.day": "99" } form = FileForm(input_data) form.validate() # False for f in form: print(f.name, f.value) # name foo.txt # ctime.year 2000 # ctime.month @@ # ctime.day 99
また、各フィールドの脇にエラーメッセージを付記したいという場合もあるため以下の様な形でエラーメッセージを触れると良さそうです。
print(form.ctime.month.errors) # ["invalid literal for int() with base 10: '@@'"] print(form.ctime.errors) # ["invalid paramaters: OrderedDict([('year', 2000), ('month', None), ('day', 99)])"]