Pythonで学ぶ 家計簿アプリ開発(第3回)~ フォームで支出を追加・削除する ~

パソコン Uncategorized

はじめに

前回は、Pythonのコード内に書いた固定データを、テンプレートを使って一覧表示するところまでを確認した。今回は、その一覧にフォームを追加し、支出データを自分で追加・削除できるようにする。

なお、今回もデータの保存先はPythonのリストのままである。そのため、アプリを再起動すると、追加・削除した内容は消えて初期状態のデータに戻ってしまう。この「消えてしまう」問題は、次回SQLiteを導入することで解決する。

今回やること

  • HTMLフォームから、Flask側にデータを送る仕組みを知る
  • 支出を追加するフォームを作る
  • 支出を削除するボタンを作る

GETとPOST

ブラウザがサーバーにリクエストを送る方法にはいくつか種類があるが、よく使うのはGETPOSTの2つである。

  • GET:サーバーから情報を取得するためのリクエスト。ページの表示などに使う。
  • POST:サーバーにデータを送信して、登録・更新・削除などの処理を依頼するためのリクエスト。

今回、支出の追加・削除はどちらも「データを送って処理してもらう」操作なので、POSTを使う。

コード解説

app.py

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

expenses = [
    {"date": "2026-08-01", "category": "食費", "item": "スーパー", "amount": 3200},
    {"date": "2026-08-03", "category": "交通費", "item": "電車", "amount": 480},
    {"date": "2026-08-05", "category": "娯楽費", "item": "映画", "amount": 1800},
]


@app.route("/")
def index():
    return render_template("index.html", expenses=expenses)


@app.route("/add", methods=["POST"])
def add():
    expenses.append({
        "date": request.form["date"],
        "category": request.form["category"],
        "item": request.form["item"],
        "amount": int(request.form["amount"]),
    })
    return redirect(url_for("index"))


@app.route("/delete/<int:index>", methods=["POST"])
def delete(index):
    expenses.pop(index)
    return redirect(url_for("index"))

前回までと違うポイントを説明する。

  • @app.route("/add", methods=["POST"])methods"POST"を指定することで、このURLはPOSTリクエストしか受け付けないようにしている
  • request.form["date"]:フォームから送られてきたデータを取り出している。request.formには、フォームのname属性をキーとした値が入っている
  • expenses.append(...):取り出したデータを、辞書としてexpensesリストに追加している
  • redirect(url_for("index")):処理が終わったら、一覧ページ(index)へ転送している。フォーム送信のたびに新しいページが表示されるのを防ぐための、定番の書き方である
  • /delete/<int:index>:URLの一部(<int:index>)を、そのまま関数の引数として受け取れるようにしている。「何番目のデータを消すか」を、URLで指定する仕組みである

※今回は学習用の簡単なアプリなので、存在しない番号が指定された場合のエラー処理は省略している。

templates/index.html(抜粋)

<td>
  <form action="{{ url_for('delete', index=loop.index0) }}" method="post">
    <button type="submit">削除</button>
  </form>
</td>
<form action="{{ url_for('add') }}" method="post">
  <label>日付 <input type="date" name="date" required></label>
  <label>カテゴリ <input type="text" name="category" required></label>
  <label>内容 <input type="text" name="item" required></label>
  <label>金額 <input type="number" name="amount" required></label>
  <button type="submit">追加</button>
</form>
  • loop.index0:Jinja2の{% for %}ループの中で使える特別な変数で、「何番目の要素か」を0から数えた数字で取得できる。これをdeleteのURLに埋め込むことで、「削除ボタンごとに、削除対象の番号を切り替える」ということを実現している
  • <input>name属性(datecategoryなど)が、app.py側のrequest.form["date"]などと対応している
    今回はフォーム処理の基本に集中するため、サーバー側の詳細な入力チェックは省略している。実用的なアプリでは、サーバー側でも入力値の検証が必要である。

動作確認

flask --app app run --debugで起動し、ブラウザで一覧の下に追加フォームが表示されていることを確認する。日付・カテゴリ・内容・金額を入力して「追加」を押すと、一覧に新しい行が増える。行の右にある「削除」ボタンを押すと、その行だけが消えることも確認しておく。

今回はデータをPythonのリストに保存しているため、アプリを再起動すると、プログラム起動時に作られた初期状態のリストが再び作られる。追加したデータが消えてしまっても、これは想定どおりの動作である。

今回のまとめ

  • フォームからデータを送るには、methods=["POST"]を指定したルートと、request.formを使う
  • 処理後はredirect(url_for(...))で一覧ページへ戻すのが定番
  • URLの一部を<int:index>のように受け取ることで、「どのデータに対する操作か」をURLで表現できる

次回予告

次回は、いよいよSQLiteを使ってデータを永続化する。これまでの「再起動すると消える」問題を解決し、本当の意味でデータが保存されるアプリにしていく。

ソースコード

GitHub:pc-labo-python-learning/03_add-delete-expense

コメント

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