バリデーションガイド
validkit-py と組み合わせることで、NanaSQLite は SQLite に書き込む前にすべての値をスキーマ検証できます。
このガイドでは、最近追加された validator / coerce 機能の実践的な使い方をまとめます。パラメータの完全な仕様は NanaSQLite API リファレンス を参照してください。
どんなときに使うか
バリデーションは次のようなケースで役立ちます。
- 不正なレコードを保存前に弾きたい
- テーブルごとのデータ構造を一貫させたい
"42"のような文字列入力を型付きの値へ安全に変換したい- サブテーブルごとに異なるスキーマを適用したい
インストール
バリデーション機能を有効にするには、extra 付きでインストールします。
bash
pip install nanasqlite[validation]実行時には、オプション依存が利用可能かどうかを確認できます。
python
from nanasqlite import HAS_VALIDKIT
if HAS_VALIDKIT:
print("validkit-py が利用可能です")
else:
print("nanasqlite[validation] をインストールしてください")基本的なスキーマ検証
validator に validkit のスキーマを渡します。
python
from validkit import v
from nanasqlite import NanaSQLite, NanaSQLiteValidationError
schema = {
"name": v.str(),
"age": v.int().range(0, 150),
"active": v.bool(),
}
db = NanaSQLite("users.db", validator=schema)
db["alice"] = {"name": "Alice", "age": 30, "active": True} # OK
try:
db["bob"] = {"name": "Bob", "age": "invalid", "active": True}
except NanaSQLiteValidationError as exc:
print(f"バリデーション失敗: {exc}")
print("DB には何も書き込まれていません")coerce による自動変換
coerce=True を指定すると、validkit-py が返した変換済みの値を NanaSQLite が保存します。ただし、実際に変換を行うにはフィールド側でも .coerce() を指定する必要があります。
python
from validkit import v
from nanasqlite import NanaSQLite
schema = {
"age": v.int().coerce(),
"score": v.float().coerce(),
}
db = NanaSQLite("scores.db", validator=schema, coerce=True)
db["player1"] = {"age": "20", "score": "9.5"}
print(db["player1"]) # {"age": 20, "score": 9.5}フィールド側で .coerce() を指定していない場合、coerce=True だけでは変換されません。
python
from validkit import v
from nanasqlite import NanaSQLite, NanaSQLiteValidationError
schema = {"age": v.int()}
db = NanaSQLite("bad.db", validator=schema, coerce=True)
try:
db["player1"] = {"age": "20"}
except NanaSQLiteValidationError:
print("フィールドバリデーターが文字列入力を拒否します")テーブルごとのバリデーション
サブテーブルごとに別々のスキーマを適用できます。子テーブルは親のスキーマを自動継承することも可能です。
python
from validkit import v
from nanasqlite import NanaSQLite
user_schema = {"name": v.str(), "age": v.int()}
score_schema = {"player": v.str(), "score": v.float().range(0.0, 100.0)}
db = NanaSQLite("app.db", validator=user_schema)
users_db = db.table("users") # user_schema を継承
scores_db = db.table("scores", validator=score_schema)
cache_db = db.table("cache", validator=None) # ここでは無効化
users_db["u1"] = {"name": "Alice", "age": 30}
scores_db["s1"] = {"player": "Alice", "score": 98.5}
cache_db["raw"] = {"anything": "goes"}テーブル単位の coerce も同じ考え方です。
python
from validkit import v
from nanasqlite import NanaSQLite
db = NanaSQLite("app.db")
coerce_schema = {"age": v.int().coerce()}
coerce_db = db.table("users_import", validator=coerce_schema, coerce=True)
coerce_db["u2"] = {"age": "31"} # {"age": 31} として保存batch_update() とバリデーション
validator が設定されている場合、batch_update() は書き込み前にバッチ全体を検証します。1件でも失敗すると、全件が拒否されます。
python
from validkit import v
from nanasqlite import NanaSQLite, NanaSQLiteValidationError
schema = {"name": v.str(), "age": v.int()}
db = NanaSQLite("batch.db", validator=schema)
try:
db.batch_update({
"u1": {"name": "Alice", "age": 30},
"u2": {"name": "Bob", "age": "bad"},
})
except NanaSQLiteValidationError:
print("アトミックに失敗し、何も書き込まれません")エラーハンドリングのポイント
- ユーザー入力を扱う箇所では
NanaSQLiteValidationErrorを捕捉する - スキーマはそのテーブルを所有するコードの近くに置く
- 構造の決まらないテーブルでは
validator=Noneを明示する - 自動変換は、意図的に正規化したい入力形式にだけ使う
例外処理のパターンは エラーハンドリングガイド を参照してください。