logo

CSVのバリデーションでCerberusが使いやすかった件

2022-12-12
2 years ago

開発環境

  • Python 3.8
  • Cerberus 1.3.4

前提

導入部分やデフォルトのバリデーションルールについては触れていませんのでご了承ください。

本題

早速ですが、アップロードしたCSVに対するバリデーションを実装する必要があり試しにCerberus触ってみたところ予想以上に使いやすかった!ということで、今回はその使いやすかった点3つを紹介します。

  • CSVの入力値の変換が容易
  • 独自のエラーメッセージを定義できる
  • 独自にバリデーションルールを定義できる

CSVの入力値の変換が容易

バリデーションを行う上でschemaをフィールドごとに定義していくことになりますが、coerceを利用することで入力値に対して変換処理を行えます。

# booleanに変換する関数(True、trueどちらにも対応したサンプル)
>>> to_bool = lambda v: v.lower() in ("True", "true")
# coerceを利用したschema
>>> v.schema = {"delete_flg": {"type": "boolean", "coerce": (str, to_bool)}}
# 入力値に対するバリデーション実行
>>> v.validate({"delete_flg": "True"})
True
# バリデーション実行後のデータ
>>> v.document
{"delete_flg": True}

バリデーションをパスした後のデータがちゃんとbooleanになっています。

ここで変換できるとDBへの登録など、後の処理が楽になります。

また、入力値が空文字(””)のとき、Noneに変換したいなんてときは以下のような関数も使えそうです。

to_none = lambda x: None if x == "" else x

独自のエラーメッセージを定義できる

schemaで定義した諸々のバリデーションルールに対するエラーメッセージは基本的には固定のメッセージ(英語)になりますが、日本語化したいケースもあると思います。

そんなときはBasicErrorHandlerをカスタムすることが可能です。

from cerberus import Validator
from cerberus.errors import BasicErrorHandler

class CustomErrorHandler(BasicErrorHandler):
    def __init__(self, tree=None):
        super(CustomErrorHandler, self).__init__(tree)
        self.messages = {
            0x00: "{0}",
            0x01: "document is missing",
            0x02: "'{field}'は必須項目です",
            ... (省略) ...
            0x94: "one or more definitions don't validate"
        }

schema = {
    {"name" : {"type" : "string", "required": True}
}
data = {
    "id" : "2", # 0x02: "name"がないのでバリデーションエラー
}

# 拡張したBasicErrorHandlerを指定
v = Validator(schema, error_handler=CustomErrorHandler())
v.validate(data)
>>> v.errors
nameは必須項目です

独自にバリデーションルールを定義できる

validatorを利用することで独自のルールを追加できます。

例えばfloat型のデータに対して、小数点第2位までにしたいなんてときは以下のようなことができます。

def validator_price(field, value, error):
		# 入力値がなければpass
		if not value:
				pass
		splited_nums = str(value).(".")
		if len(splited_nums) > 1 and len(splited_nums[1] > 2):
				# error()の第2引数に独自のエラーメッセージを設定できます
				error(field, "小数点は第2位までです")

schema = {
    {"price" : {"type" : "float", "validator": validator_price}
}

さいごに

使う頻度高めだろうなという3点を紹介しました。

これから使ってみようかなと考えている人の参考になると幸いです!

参照