🎉 初心者のアプリ制作100日チャレンジ【Day77】エラー・空状態に対応する〜「壊れにくいアプリ」に仕上げる〜
💡 はじめに
Day76で、
検索・絞り込みまでできるようになりました。
ここまで来ると、
アプリはもう 8割完成 しています。
残りの2割は何か。
それが
「うまくいかなかった時にどう振る舞うか」 です。
🎯 今日のゴール(Day77)
未入力・失敗時に適切な表示ができる
データ0件でも違和感がない
ユーザーが不安にならないUIになる
「業務で使える」安心感を出す
❌ よくある惜しいアプリ
何も起きない
画面が真っ白
エラーがそのまま表示される
作った本人は分かるけど、
使う側は不安になります。
🧠 今日の考え方(超重要)
今日のキーワードはこれです。
「失敗は想定内。沈黙はNG。」
① 未入力エラーを“ちゃんと伝える”
❌ ありがちな書き方
if submitted:
insert_drug(name, memo)
これだと
空文字でも保存しに行きます。
✅ 改善した書き方
if submitted:
if name.strip() == "":
st.warning("薬品名を入力してください")
else:
insert_drug(name, memo)
st.success("DBに保存しました")
ポイント:
strip() で空白だけも防ぐ
warning はエラーより優しい
何をすればいいかが分かる
② データ0件でも「壊れてない」表示
❌ 何も表示しない
if rows:
st.table(rows)
これだと
「壊れた?」と思われます。
✅ 空状態を明示する
if len(rows) == 0:
st.info("まだデータは登録されていません")
else:
st.table(rows)
これだけで
安心感が段違い です。
③ 検索結果が0件の時
Day76の検索に
ひと工夫します。
if search_word and len(rows) == 0:
st.info("検索条件に一致するデータはありません")
elif len(rows) == 0:
st.info("まだデータは登録されていません")
else:
st.table(rows)
検索した結果0件
そもそも0件
この2つを
ちゃんと分ける のがポイント。
④ DBエラーを画面に出さない
SQLiteは基本安定していますが、
万一に備えます。
def safe_insert_drug(name, memo):
try:
insert_drug(name, memo)
return True
except Exception:
return False
if submitted:
if name.strip() == "":
st.warning("薬品名を入力してください")
else:
success = safe_insert_drug(name, memo)
if success:
st.success("DBに保存しました")
else:
st.error("保存に失敗しました。再度お試しください")
これで
生のエラーをユーザーに見せない。
✅ Day77 完成コード(エラー対応込み)
Day76のコードを
これに置き換えてOK です。
import streamlit as st
import sqlite3
from datetime import date
import os
DB_DIR = "data"
DB_PATH = os.path.join(DB_DIR, "app.db")
def init_db():
os.makedirs(DB_DIR, exist_ok=True)
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS drugs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
memo TEXT,
created DATE
)
""")
conn.commit()
conn.close()
def insert_drug(name, memo):
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
cur.execute(
"INSERT INTO drugs (name, memo, created) VALUES (?, ?, ?)",
(name, memo, date.today())
)
conn.commit()
conn.close()
def safe_insert_drug(name, memo):
try:
insert_drug(name, memo)
return True
except Exception:
return False
def search_drugs(keyword):
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
if keyword:
cur.execute(
"""
SELECT id, name, memo, created
FROM drugs
WHERE name LIKE ?
ORDER BY id DESC
""",
(f"%{keyword}%",)
)
else:
cur.execute(
"""
SELECT id, name, memo, created
FROM drugs
ORDER BY id DESC
"""
)
rows = cur.fetchall()
conn.close()
return rows
st.title("新薬情報 管理アプリ")
init_db()
with st.form("drug_form"):
name = st.text_input("薬品名(必須)")
memo = st.text_area("補足メモ(任意)")
submitted = st.form_submit_button("登録する")
if submitted:
if name.strip() == "":
st.warning("薬品名を入力してください")
else:
if safe_insert_drug(name, memo):
st.success("DBに保存しました")
else:
st.error("保存に失敗しました")
st.divider()
st.subheader("登録一覧")
search_word = st.text_input("薬品名で検索")
rows = search_drugs(search_word)
if search_word and len(rows) == 0:
st.info("検索条件に一致するデータはありません")
elif len(rows) == 0:
st.info("まだデータは登録されていません")
else:
st.table(rows)
👀 何が良くなったか
失敗しても沈黙しない
状況が言葉で分かる
ユーザーが迷わない
この時点で
「業務に出せる最低ライン」 に到達しています。
🌱 今日のまとめ(Day77)
エラーは想定内
空状態は必ず説明する
沈黙よりメッセージ
完成度は細部で決まる
🔮 次回予告(Day78)
次は、
UIの最終整理
表示の順番
見た目の整え方
「触って気持ちいい」仕上げ
