状態の選択肢を画面で制限しても、別の登録経路から違う値が入ることがあります。CSV取り込みや補助スクリプトもある台帳では、画面の選択リストだけに条件を置くと、保存先の値がそろわなくなります。
SQLiteのCHECK制約を使うと、保存できる値をテーブル側でも制限できます。状態がopenとdoneだけなら、その条件を定義します。NULLを認めない条件はNOT NULLとして別に指定します。
不正な状態とNULLを拒否する
次はメモリー上の架空DBです。処理が終わると残らず、既存の台帳は開きません。正常な一行を保存した後、holdとNULLを試します。
import sqlite3
db = sqlite3.connect(":memory:")
try:
db.execute(
"CREATE TABLE tasks ("
"id TEXT PRIMARY KEY NOT NULL,"
"status TEXT NOT NULL CHECK(status IN ('open','done')))"
)
with db:
db.execute("INSERT INTO tasks VALUES (?,?)", ("T1", "open"))
for key, value, message in [
("T2", "hold", "許可していない状態を拒否"),
("T3", None, "NULLの状態を拒否"),
]:
try:
with db:
db.execute("INSERT INTO tasks VALUES (?,?)", (key, value))
except sqlite3.IntegrityError:
print(message)
else:
raise AssertionError("制約を通過しました")
rows = db.execute("SELECT id,status FROM tasks ORDER BY id").fetchall()
print("保存した行:", rows)
assert rows == [("T1", "open")]
finally:
db.close()
holdとNULLはそれぞれ拒否され、T1のopenだけが残ります。Windows・Python 3.12.10・SQLite 3.49.1で、この結果を確認しました。実業務の状態や既存DBを書き換えた例ではありません。
CHECKは、式が偽の値になった入力を拒否します。NULLが入った場合の評価は別なので、必須項目ならNOT NULLも書きます。画面で必須にしたことを、保存先の必須条件の代わりにはしません。
状態の値と変更できる人は別
この制約はopenかdoneかという値の種類を制限するものです。誰がdoneへ変更してよいか、doneからopenへ戻せるか、承認を経たかまでは確認していません。
登録前のPython側でも値を調べれば、利用者へ読みやすい理由を返せます。DB側の制約は別の登録経路でも同じ基本条件を確認するために使えます。片方だけで十分と決めず、役割を分けます。
既存台帳へそのまま適用しない
上のCREATE TABLEは練習用の新しいテーブルを作っています。すでに使っている台帳へ条件を足す作業は、既存の値、登録経路、停止時間、バックアップなどの確認を伴います。このページでは移行処理は実行しません。
許可する状態を増やす場合も、DBの条件だけを変えれば終わりではありません。画面、入力検査、集計、通知で新しい状態をどう扱うかを合わせます。状態が未知だった場合にopenへ置き換えると、確認が必要な入力まで正常扱いになるので、理由を返して止める形を検討します。
最初は正常値、未知の文字、空文字、NULLを含む小さな入力で試します。どの値を許容するかを業務側で決めたうえで、その条件と保存結果を比べてください。
接続と検索はSQLite入門、台帳の状態を決める考え方は在庫・台帳の自動化設計で補えます。
参考資料
SQLite CREATE TABLEとPython sqlite3の公式資料を2026年11月1日に確認しました。
