Python | 1 日 120 分 × 7 日アプリ学習:JSON保存アプリ(中級編)

Web APP Python
スポンサーリンク

3日目のゴール

3日目のテーマは
「JSONに保存されたデータを“探す・直す・消す”ことができるようにすること」 です。

1日目:JSONという形式を知り、Pythonのdict / listとの対応を理解した。
2日目:ユーザーが入力したメモをJSONに保存し、起動時に復元できるようにした。

3日目では、そこから一歩進んで、

  • あるメモを「特定して取り出す」
  • そのメモの内容を「更新する」
  • いらないメモを「削除する」

という、アプリに必須の操作を身につけます。
これはいわゆる CRUD(Create / Read / Update / Delete) のうち、
「Update」と「Delete」をしっかり押さえる日です。


2日目までのメモアプリを振り返る

データの形をもう一度確認する

2日目のメモアプリでは、メモ1件をこう表現していました。

memo = {
    "text": "牛乳を買う"
}
Python

複数のメモはリストで持ちます。

memos = [
    {"text": "牛乳を買う"},
    {"text": "Pythonの勉強を30分する"}
]
Python

JSONファイルではこうなります。

[
  {
    "text": "牛乳を買う"
  },
  {
    "text": "Pythonの勉強を30分する"
  }
]

2日目では、

  • 起動時に memos.json から読み込む
  • メモを追加して保存する
  • 一覧表示する

ところまでできました。

3日目では、この memos に対して
「どのメモを更新するか」「どのメモを削除するか」を
きちんと指定できるようにしていきます。


「どのメモか」を特定する方法を決める

インデックスで指定するか、IDを持たせるか

メモを更新・削除するには、
まず「どのメモを対象にするか」を決める必要があります。

やり方は大きく2つあります。

1つ目は、「表示時の番号(インデックス)で指定する」方法です。
2日目の show_memos は、こうなっていました。

def show_memos(memos):
    if not memos:
        print("メモはまだありません。")
        return
    print("=== メモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        print(f"{i}. {memo['text']}")
Python

ここで表示している i(1, 2, 3, …)を使って
「3番のメモを消したい」と指定することができます。

2つ目は、「メモにIDを持たせる」方法です。

memo = {
    "id": 1,
    "text": "牛乳を買う"
}
Python

ID方式は本格的ですが、
中級の入り口としては「番号指定」で十分です。
今日は、番号で指定するスタイル で進めます。


メモを削除する処理を作る

インデックスを使ってリストから取り除く

まずは「削除」からやってみましょう。
やることはシンプルで、

  • 一覧を見せる
  • ユーザーに「何番を消すか」聞く
  • その番号のメモを memos から削除する
  • JSONに保存し直す

という流れです。

def delete_memo(memos):
    if not memos:
        print("削除できるメモがありません。")
        return

    print("=== 削除対象のメモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        print(f"{i}. {memo['text']}")

    num_text = input("削除するメモの番号を入力してください: ")

    if not num_text.isdigit():
        print("数字を入力してください。")
        return

    num = int(num_text)

    if num < 1 or num > len(memos):
        print("その番号のメモはありません。")
        return

    deleted = memos.pop(num - 1)
    print(f"削除しました: {deleted['text']}")
Python

ここで重要なポイントを深掘りします。

1つ目は、必ず範囲チェックをしている ことです。
num が 1〜len(memos) の範囲外なら、
memos[num - 1] はエラーになります。
アプリが落ちないように、
「その番号のメモはありません」と丁寧に返しています。

2つ目は、pop(num - 1) を使っていることです。
pop は「取り出して削除する」メソッドで、
削除した要素を戻り値として返してくれます。
これを使うことで、
「どのメモを消したか」をメッセージに表示できます。

3つ目は、「削除したらまだ保存していない」という点です。
ここではまだ save_memos を呼んでいません。
実際のアプリでは、
delete_memo のあとに save_memos(memos) を呼ぶことで
JSONファイルにも反映させます。


メモを編集(更新)する処理を作る

既存のメモの内容を書き換える

次に、「メモの内容を変更する」処理を作ります。
流れは削除とよく似ています。

  • 一覧を見せる
  • ユーザーに「何番を編集するか」聞く
  • 新しいテキストを入力してもらう
  • そのメモの text を上書きする
def edit_memo(memos):
    if not memos:
        print("編集できるメモがありません。")
        return

    print("=== 編集対象のメモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        print(f"{i}. {memo['text']}")

    num_text = input("編集するメモの番号を入力してください: ")

    if not num_text.isdigit():
        print("数字を入力してください。")
        return

    num = int(num_text)

    if num < 1 or num > len(memos):
        print("その番号のメモはありません。")
        return

    old_text = memos[num - 1]["text"]
    print(f"現在の内容: {old_text}")
    new_text = input("新しい内容を入力してください(空なら変更しません): ")

    if new_text == "":
        print("変更をキャンセルしました。")
        return

    memos[num - 1]["text"] = new_text
    print("メモを更新しました。")
Python

ここでの重要ポイントは2つです。

1つ目は、「今の内容を見せてから編集させている」ことです。
ユーザーは「どのメモを触っているか」を確認できるので、
操作ミスが減ります。

2つ目は、「空文字なら変更しない」というルールです。
うっかり Enter だけ押してしまったときに、
メモが空になってしまうのを防げます。


検索:特定の文字を含むメモだけを表示する

「探す」機能を入れると一気にアプリっぽくなる

3日目のうちに、
簡単な「検索」も入れてしまいましょう。

やりたいことは、

  • キーワードを入力してもらう
  • その文字を含むメモだけを表示する

というものです。

def search_memos(memos):
    keyword = input("検索キーワードを入力してください: ")
    if keyword == "":
        print("キーワードが空です。")
        return

    results = []
    for memo in memos:
        if keyword in memo["text"]:
            results.append(memo)

    if not results:
        print("そのキーワードを含むメモはありません。")
        return

    print("=== 検索結果 ===")
    for i, memo in enumerate(results, start=1):
        print(f"{i}. {memo['text']}")
Python

ここでのポイントは、

  • 完全一致ではなく「含まれているかどうか」で探している
  • 結果が0件のときも、ちゃんとメッセージを出している

という2点です。

この「検索」は、
後でToDoアプリや本の管理アプリなどに発展させるときにも
そのまま使えるパターンです。


3日目のミニアプリ:編集・削除・検索つきメモJSONアプリ

2日目のアプリに機能を足した全体像

ここまでの関数を組み合わせて、
3日目版のアプリを1本にまとめてみます。

import json
import os

FILENAME = "memos.json"

def load_memos():
    if not os.path.exists(FILENAME):
        return []
    with open(FILENAME, "r", encoding="utf-8") as f:
        return json.load(f)

def save_memos(memos):
    with open(FILENAME, "w", encoding="utf-8") as f:
        json.dump(memos, f, ensure_ascii=False, indent=2)

def add_memo(memos):
    text = input("メモを入力してください: ")
    if text == "":
        print("空のメモは追加しません。")
        return
    memo = {"text": text}
    memos.append(memo)
    print("メモを追加しました。")

def show_memos(memos):
    if not memos:
        print("メモはまだありません。")
        return
    print("=== メモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        print(f"{i}. {memo['text']}")

def delete_memo(memos):
    if not memos:
        print("削除できるメモがありません。")
        return

    show_memos(memos)
    num_text = input("削除するメモの番号を入力してください: ")

    if not num_text.isdigit():
        print("数字を入力してください。")
        return

    num = int(num_text)

    if num < 1 or num > len(memos):
        print("その番号のメモはありません。")
        return

    deleted = memos.pop(num - 1)
    print(f"削除しました: {deleted['text']}")

def edit_memo(memos):
    if not memos:
        print("編集できるメモがありません。")
        return

    show_memos(memos)
    num_text = input("編集するメモの番号を入力してください: ")

    if not num_text.isdigit():
        print("数字を入力してください。")
        return

    num = int(num_text)

    if num < 1 or num > len(memos):
        print("その番号のメモはありません。")
        return

    old_text = memos[num - 1]["text"]
    print(f"現在の内容: {old_text}")
    new_text = input("新しい内容を入力してください(空なら変更しません): ")

    if new_text == "":
        print("変更をキャンセルしました。")
        return

    memos[num - 1]["text"] = new_text
    print("メモを更新しました。")

def search_memos(memos):
    keyword = input("検索キーワードを入力してください: ")
    if keyword == "":
        print("キーワードが空です。")
        return

    results = []
    for memo in memos:
        if keyword in memo["text"]:
            results.append(memo)

    if not results:
        print("そのキーワードを含むメモはありません。")
        return

    print("=== 検索結果 ===")
    for i, memo in enumerate(results, start=1):
        print(f"{i}. {memo['text']}")

def main():
    memos = load_memos()
    print("JSONファイルからメモを読み込みました。")

    while True:
        print("\n=== メニュー ===")
        print("1: メモを追加")
        print("2: メモ一覧を表示")
        print("3: メモを編集")
        print("4: メモを削除")
        print("5: メモを検索")
        print("0: 終了")

        choice = input("番号を選んでください: ")

        if choice == "1":
            add_memo(memos)
            save_memos(memos)

        elif choice == "2":
            show_memos(memos)

        elif choice == "3":
            edit_memo(memos)
            save_memos(memos)

        elif choice == "4":
            delete_memo(memos)
            save_memos(memos)

        elif choice == "5":
            search_memos(memos)

        elif choice == "0":
            save_memos(memos)
            print("終了します。")
            break

        else:
            print("正しい番号を入力してください。")

main()
Python

ここまで来ると、
もう立派な「メモ管理アプリ」です。


3日目で絶対に押さえてほしい本質

JSON保存アプリの「操作の型」を身につける

今日の本質は、次の流れです。

  • JSONから読み込んだデータは、Pythonのlist / dictとして扱える
  • そのlistに対して「どの要素を触るか」を決める
  • 触る対象が決まれば、あとは 削除 でも 更新 でも同じパターン

特に大事なのは、

  • 削除は「インデックスを使って pop する」
  • 更新は「対象を見つけて、その中の値を書き換える」
  • 変更したら save_memos でJSONに書き戻す

という「型」を体で覚えることです。

この型さえ身につけば、
メモアプリだけでなく、
ToDoアプリ、ブックマーク管理、買い物リスト、
どんなJSON保存アプリにも応用できます。


次の日へのつながり

4日目では、
今日までに作った機能を「役割ごとに整理する」ことで、
コードを読みやすく・壊れにくくしていきます。

もし余裕があれば、
今日のメモアプリに「作成日時」や「重要フラグ」を足してみてください。
それだけで、JSONが
「あなたが設計した小さなデータベース」
に見えてくるはずです。

タイトルとURLをコピーしました