データ入力フォームと一覧表示(主要機能の実現)
この週の目標は、Webページ上で家計簿の項目を入力し、それがデータベースに保存され、そしてすぐに画面に表示されるという、家計簿アプリとしての最初の「使える形」を目にすることです。
1.Webフォームの作成 (Flask-WTFの導入)
ユーザーが家計簿のデータを入力するためには、Webページ上に「入力フォーム」が必要です。HTMLだけでもフォームは作れますが、FlaskではFlask-WTFという拡張機能を使うと、フォームの作成、送信されたデータの受け取り、入力値の検証(バリデーション)などを非常に簡単に行うことができます。wft…。
1-1. Flask-WTF のインストール
pip install Flask-WTF
バージョン記録 Flask-WTF-1.2.2

1-2. app.py
の設定変更とフォームクラスの定義
from flask import Flask, render_template, request, redirect, url_for # request, redirect, url_for を追加
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm # FlaskForm をインポート
from wtforms import StringField, IntegerField, DateField, SubmitField # フォーム部品をインポート
from wtforms.validators import DataRequired, NumberRange # バリデーションルールをインポート
import datetime # 日付操作のためにインポート
app = Flask(__name__)
# データベース設定の追加
# SQLiteデータベースファイルのパスを指定
# 'sqlite:///site.db' は、プロジェクトフォルダ内に 'site.db' という名前のデータベースファイルを作成することを意味します。
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
# 追跡機能を無効にする(リソース節約のため)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# SQLAlchemyオブジェクトの初期化
db = SQLAlchemy(app)
# 家計簿の「記録」を保存するためのデータ構造(テーブルの設計図)をPythonコードで定義します
class Transaction(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.String(20), nullable=False) # 日付 (例: '2025-07-01')
item = db.Column(db.String(100), nullable=False) # 項目名 (例: '食費', '交通費')
amount = db.Column(db.Integer, nullable=False) # 金額 (整数)
def __repr__(self):
return f"Transaction('{self.date}', '{self.item}', {self.amount})"
#フォームクラスの定義
class TransactionForm(FlaskForm):
# 日付フィールド:日付を選択できるようにします
date = DateField('日付', format='%Y-%m-%d', default=datetime.date.today, validators=[DataRequired()])
# 項目フィールド:テキスト入力
item = StringField('項目', validators=[DataRequired()])
# 金額フィールド:整数入力、0以上であることを要求
amount = IntegerField('金額', validators=[DataRequired(), NumberRange(min=0)])
# 送信ボタン
submit = SubmitField('追加')
#
@app.route("/")
def index():
return render_template("index.html")
@app.route("/about") # 新しいルーティングを追加
def about():
#return "<h1>このアプリについて</h1><p>これは私の初めてのFlaskアプリです。</p>"
return render_template("about.html") #HTMLテンプレートをレンダリングして返す
@app.route("/greet/<string:name>") # <name> 部分がURLパラメータ
def greet(name): # 関数にパラメータを受け取る引数を追加
# 変更前: return f"<h1>Hello, {name}!</h1><p>Welcome to your personalized page!</p>"
# 変更後: render_template を使って greet.html にデータを渡す
return render_template("greet.html", username=name) # usernameという名前で name の値を渡す
@app.route("/user/<int:user_id>")
def show_user(user_id):
return f"ユーザーIDは {user_id} です。"
@app.route("/calculate/<num1>/<num2>")
def caluculate(num1,num2):
try:
# URLパラメータは文字列として受け取られるので、int()で数値に変換
number1 = int(num1)
number2 = int(num2)
total = number1 + number2
# calculate.html に計算結果を渡してレンダリング
return render_template("calculate.html",num1 = number1,num2 = number2,total = total)
except ValueError:
# エラーページをレンダリングすることも可能ですが、今回は直接メッセージを返します
return "エラー: 無効な数値が指定されました。", 400
@app.route("/product/<id>/<name>")
def product(id,name):
return render_template("product.html", product_id = id, product_name = name)
if __name__ == '__main__':
# データベースの初回作成・更新が必要な場合は、コメントを外して実行してください(一度実行したらコメントアウトしてOK)
# with app.app_context():
# db.create_all()
app.run(debug=True)
2.家計簿入力ページのルーティングと表示
次に、家計簿の入力フォームを表示するための新しいルーティングと関数をapp.py
に追加します。
# ... 省略(既存のコード) ...
# 家計簿の追加・表示ページ
@app.route("/add_transaction", methods=['GET', 'POST']) # GETとPOSTの両方のリクエストを受け付ける
def add_transaction():
form = TransactionForm() # フォームのインスタンスを作成
# フォームが送信され、かつ入力が有効な場合
if form.validate_on_submit():
# フォームからデータを受け取る
new_transaction = Transaction(
date=form.date.data,
item=form.item.data,
amount=form.amount.data
)
# データベースに追加・保存
db.session.add(new_transaction)
db.session.commit()
# 追加後に一覧ページにリダイレクト
return redirect(url_for('add_transaction')) # 同じページにリダイレクトすることでフォームをリセット
# GETリクエストまたはフォームが検証に失敗した場合
# データベースから全ての取引データを読み出す
all_transactions = Transaction.query.order_by(Transaction.date.desc()).all() # 日付の新しい順に並べ替え
return render_template("add_transaction.html", form=form, transactions=all_transactions)
# ... 省略(既存のコード) ...
3. add_transaction.html
ファイルの作成
次に、templates
フォルダの中に、家計簿の入力フォームと一覧表示を行うadd_transaction.html
という新しいHTMLテンプレートファイルを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>家計簿アプリ - 記録の追加と一覧</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>家計簿アプリ</h1>
<p><a href="/">トップページに戻る</a> | <a href="/about">このアプリについて</a></p>
<h2>新しい記録を追加</h2>
{# Flask-WTFフォームの開始。actionは空で同じURLに送信、methodはPOST #}
<form method="POST" action="">
{# フォームのセキュリティトークン(必須) #}
{{ form.csrf_token }}
{# 日付フィールド #}
<div>
{{ form.date.label }}<br>
{{ form.date() }}
{% if form.date.errors %}
<ul class="errors">
{% for error in form.date.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 項目フィールド #}
<div>
{{ form.item.label }}<br>
{{ form.item() }}
{% if form.item.errors %}
<ul class="errors">
{% for error in form.item.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 金額フィールド #}
<div>
{{ form.amount.label }}<br>
{{ form.amount() }}
{% if form.amount.errors %}
<ul class="errors">
{% for error in form.amount.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 送信ボタン #}
<div>
{{ form.submit() }}
</div>
</form>
<hr> {# 区切り線 #}
<h2>これまでの記録</h2>
{% if transactions %} {# 記録がある場合のみ表示 #}
<table border="1" style="width:100%; border-collapse: collapse;">
<thead>
<tr>
<th>日付</th>
<th>項目</th>
<th>金額</th>
</tr>
</thead>
<tbody>
{# Pythonから渡された transactions リストをループ処理で表示 #}
{% for t in transactions %}
<tr>
<td>{{ t.date.strftime('%Y-%m-%d') }}</td> {# 日付をフォーマットして表示 #}
<td>{{ t.item }}</td>
<td>{{ t.amount | int }}円</td> {# 金額を整数として表示し「円」を追加 #}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>まだ記録がありません。</p>
{% endif %}
</body>
</html>
Flaskアプリケーションを再起動して確認する
重要メモ
# ★重要★
# Transactionモデルのdateカラムの型を変更した場合
# 1. instanceフォルダ内の site.db ファイルを削除
# 2. app.py の app.run(debug=True) の前に
# with app.app_context():
# db.create_all()
# のコメントアウトを一時的に外し、一度アプリを実行しデータベースを再作成
# 3. データベースが作成されたら、上記2行はコメントアウトするか削除してOK
完成形

データの追加と表示、保存ができました。

家計簿データの「編集」と「削除」機能
1.既存の記録を「削除」する機能の実装
まずは、一番シンプルで分かりやすい「削除」機能から実装していきます。
1-1. app.py
に削除ルーティングと関数を追加
# 記録の削除
@app.route("/delete_transaction/<int:transaction_id>", methods=['POST'])
def delete_transaction(transaction_id):
transaction_to_delete = Transaction.query.get_or_404(transaction_id) # IDで記録を検索
db.session.delete(transaction_to_delete) # データベースから削除
db.session.commit() # 変更を保存
return redirect(url_for('add_transaction')) # 削除後、一覧ページにリダイレクト
1-2. add_transaction.html
に削除ボタンを追加
<tbody>
{% for t in transactions %}
<tr>
<td>{{ t.date.strftime('%Y-%m-%d') }}</td>
<td>{{ t.item }}</td>
<td>{{ t.amount | int }}円</td>
{# ★ここから追加 - 削除ボタン #}
<td>
<form method="POST" action="{{ url_for('delete_transaction', transaction_id=t.id) }}">
<input type="submit" value="削除" onclick="return confirm('本当に削除しますか?');">
</form>
</td>
{# ★ここまで追加 #}
</tr>
{% endfor %}
</tbody>
削除ボタンの作成、そして削除の実行が確認できました。
2.既存の記録を「編集」する機能の実装
次に、既存の家計簿記録を編集できるようにします。「編集」は「追加」と似ていますが、既存のデータを読み込み、それを更新する点が異なります。
2-1. app.py
に編集ルーティングと関数を追加
# ... 省略(既存の delete_transaction 関数など) ...
# 記録の編集
@app.route("/edit_transaction/<int:transaction_id>", methods=['GET', 'POST'])
def edit_transaction(transaction_id):
transaction = Transaction.query.get_or_404(transaction_id) # IDで編集対象の記録を検索
form = TransactionForm(obj=transaction) # 既存データでフォームを初期化
if form.validate_on_submit(): # フォームが送信され、かつ入力が有効な場合
# フォームのデータをデータベースの記録に更新
transaction.date = form.date.data
transaction.item = form.item.data
transaction.amount = form.amount.data
db.session.commit() # 変更を保存
return redirect(url_for('add_transaction')) # 更新後、一覧ページにリダイレクト
elif request.method == 'GET': # GETリクエストの場合 (ページ表示時)
# フォームの初期値をデータベースの記録のデータで設定
form.date.data = transaction.date
form.item.data = transaction.item
form.amount.data = transaction.amount
return render_template('edit_transaction.html', form=form, transaction=transaction)
2-2. add_transaction.html
に編集ボタンを追加
<tbody>
{% for t in transactions %}
<tr>
<td>{{ t.date.strftime('%Y-%m-%d') }}</td>
<td>{{ t.item }}</td>
<td>{{ t.amount | int }}円</td>
{# ★ここから追加 - 編集ボタン #}
<td>
<a href="{{ url_for('edit_transaction', transaction_id=t.id) }}">編集</a>
</td>
{# ★ここまで追加 #}
<td>
<form method="POST" action="{{ url_for('delete_transaction', transaction_id=t.id) }}">
<input type="submit" value="削除" onclick="return confirm('本当に削除しますか?');">
</form>
</td>
</tr>
{% endfor %}
</tbody>
2-3. edit_transaction.html
ファイルの作成
次に、templates
フォルダの中に、編集フォームを表示するためのedit_transaction.html
という新しいHTMLテンプレートファイルを作成します。
2-4. edit_transaction.html
にHTMLコードを記述する
作成したedit_transaction.html
ファイルをテキストエディタで開いて、以下のHTMLコードをコピー&ペーストして保存します。これは、基本的な構造がadd_transaction.html
のフォーム部分と似ています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>家計簿アプリ - 記録の編集</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>記録を編集</h1>
<p><a href="{{ url_for('add_transaction') }}">家計簿一覧に戻る</a></p>
{# Flask-WTFフォームの開始。actionは空で同じURLに送信、methodはPOST #}
<form method="POST" action="">
{# フォームのセキュリティトークン(必須) #}
{{ form.csrf_token }}
{# 日付フィールド #}
<div>
{{ form.date.label }}<br>
{{ form.date() }}
{% if form.date.errors %}
<ul class="errors">
{% for error in form.date.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 項目フィールド #}
<div>
{{ form.item.label }}<br>
{{ form.item() }}
{% if form.item.errors %}
<ul class="errors">
{% for error in form.item.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 金額フィールド #}
<div>
{{ form.amount.label }}<br>
{{ form.amount() }}
{% if form.amount.errors %}
<ul class="errors">
{% for error in form.amount.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
{# 送信ボタン #}
<div>
{{ form.submit(value='更新') }} {# ボタンの表示を「更新」に変更 #}
</div>
</form>
</body>
</html>
3.Flaskアプリケーションを再起動して確認する

削除と編集機能、共に実装できました。第3週はこれで終わりです。
全てのコードを把握できているわけではないですが、Geminiを駆使してエラーはすぐに解消できました。
計画の第4週までを現実では約1週間ほどで到達しました。朝と夜のまとまった時間、そして休日をしっかり使えました。最後の仕上げ期間を設け「オリジナルの応用機能」を加えることで、コードの理解と実践的なスキルを身に着けたいと考えています。
第4週では「見た目の改善」と「簡易的な集計機能」の追加を行います。
おまけ
毎度恒例、NotebookLMに読み込ませてみました。以下、音声データです。