
目標
- ファイル名のリファクタリングが行える。
- 商品データの編集と削除機能を実装できる。
Flaskアプリケーションに機能を追加する
商品データの編集と削除機能を追加する
Part.13までに作成したアプリケーションでは、商品データの編集や削除機能がありませんでした。このままだと、入力ミスがあった際に不必要なデータが残り、不便です。そこで、この記事では商品データの編集と削除機能を追加します。
ファイル名のリファクタリング
add_product.htmlファイル名のリファクタリング
Part.13までに作成したテンプレートには「add_product.html」「products.html」「category_form.html」「categories.html」「manage_products.html」があります。その中で、カテゴリーデータの追加・編集をおこなうためのテンプレートは「category_form.html」です。
現在、商品の追加・編集を行うテンプレートは「add_product.html」ですが、名前を統一するために「product_form.html」に変更します。
add_product.htmlファイルを右クリックして「Rename」をクリックします。

ファイル名を「product_form.html」に変更します。

「app.py」の次の部分を修正します。
# ルート: 商品追加ページ
@app.route("/add_product", methods=["GET", "POST"])
def add_product():
form = AddProductForm()
form.set_category_choices() # カテゴリーリストを設定
if form.validate_on_submit():
# フォームが送信され、バリデーションが成功した場合
name = form.product_name.data
price = form.product_price.data
category_id = form.category.data
# 新しい商品をデータベースに追加
new_product = Product(name=name, price=price, category_id=category_id)
db.session.add(new_product) # セッションに追加
db.session.commit() # データベースにコミット
# 商品一覧ページにリダイレクト
return redirect(url_for("product_list"))
return render_template("add_product.html", form=form)
return render_template(“add_product.html“, form=form)
↓
return render_template(“product_form.html“, form=form)
今回のファイル名のリファクタリングは以上です。
商品データの編集と削除機能の追加
app.pyファイルの編集
app.pyの編集後の内容です。商品の「編集」と「削除」のルートを作成しています。
from itertools import groupby
from flask import Flask, flash, redirect, render_template, url_for
from apps.productsapp.forms import AddProductForm, CategoryForm
from apps.productsapp.models import Category, Product, db # models.pyからインポート
# アプリケーションの初期化
app = Flask(__name__)
app.secret_key = "your_secret_key"
# SQLiteの設定
app.config["SQLALCHEMY_DATABASE_URI"] = (
"sqlite:///products.db" # SQLiteデータベースファイル
)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False # 不要な変更追跡を無効化
# SQLAlchemyのインスタンスを初期化
db.init_app(app)
# アプリケーションコンテキスト内でテーブル作成
with app.app_context():
db.create_all()
# ルート: トップページ
@app.route("/")
def index():
return "Top page"
# カテゴリー一覧を表示
@app.route("/categories")
def category_list():
categories = Category.query.all()
return render_template("categories.html", categories=categories)
# カテゴリーを追加
@app.route("/category/add", methods=["GET", "POST"])
def add_category():
form = CategoryForm()
if form.validate_on_submit():
new_category = Category(name=form.name.data)
db.session.add(new_category)
db.session.commit()
flash("カテゴリーを追加しました", "success")
return redirect(url_for("category_list"))
return render_template(
"category_form.html", form=form, title="カテゴリー追加フォーム"
)
# カテゴリーを編集
@app.route("/category/edit/<int:category_id>", methods=["GET", "POST"])
def edit_category(category_id):
category = Category.query.get_or_404(category_id)
form = CategoryForm(obj=category)
if form.validate_on_submit():
category.name = form.name.data
db.session.commit()
flash("カテゴリーを更新しました", "success")
return redirect(url_for("category_list"))
return render_template("category_form.html", form=form, title="カテゴリー編集")
# カテゴリーを削除
@app.route("/category/delete/<int:category_id>", methods=["POST"])
def delete_category(category_id):
category = Category.query.get_or_404(category_id)
# もしカテゴリーに紐づいた商品があればエラーを出す
if category.products:
flash("このカテゴリーには商品が登録されているため削除できません", "danger")
return redirect(url_for("category_list"))
db.session.delete(category)
db.session.commit()
flash("カテゴリーを削除しました", "success")
return redirect(url_for("category_list"))
# ルート: 商品一覧ページ
@app.route("/products")
def product_list():
products = Product.query.all() # SQLiteから商品データを取得
# 商品をカテゴリーごとにグループ化
grouped_products = {}
for key, group in groupby(
sorted(products, key=lambda p: p.category.name if p.category else "なし"),
key=lambda p: p.category.name if p.category else "なし",
):
grouped_products[key] = list(group)
return render_template("products.html", grouped_products=grouped_products)
# ルート: 商品追加ページ
@app.route("/add_product", methods=["GET", "POST"])
def add_product():
form = AddProductForm()
form.set_category_choices() # カテゴリーリストを設定
if form.validate_on_submit():
# フォームが送信され、バリデーションが成功した場合
name = form.product_name.data
price = form.product_price.data
category_id = form.category.data
# 新しい商品をデータベースに追加
new_product = Product(name=name, price=price, category_id=category_id)
db.session.add(new_product) # セッションに追加
db.session.commit() # データベースにコミット
# 商品一覧ページにリダイレクト
return redirect(url_for("product_list"))
return render_template("product_form.html", form=form)
# ルート: 商品を編集
@app.route("/product/edit/<int:product_id>", methods=["GET", "POST"])
def edit_product(product_id):
product = Product.query.get_or_404(product_id)
form = AddProductForm(obj=product)
form.set_category_choices() # カテゴリーリストを設定
if form.validate_on_submit():
product.name = form.product_name.data
product.price = form.product_price.data
product.category_id = form.category.data
db.session.commit()
flash("商品情報を更新しました", "success")
return redirect(url_for("product_list"))
return render_template("product_form.html", form=form, title="商品編集")
# ルート: 商品を削除
@app.route("/product/delete/<int:product_id>", methods=["POST"])
def delete_product(product_id):
product = Product.query.get_or_404(product_id)
db.session.delete(product)
db.session.commit()
flash("商品を削除しました", "success")
return redirect(url_for("product_list"))
# ルート: 商品管理ページ
@app.route("/manage_products")
def manage_products():
return render_template("manage_products.html")
if __name__ == "__main__":
app.run(debug=True)
product_form.htmlの修正
product_form.htmlの修正後の内容。inputタグのvalueの値について「”商品を登録する”」と変更しています。
<!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='css/style.css') }}">
</head>
<body>
<h1>商品追加ページ</h1>
<ul>
<li><a href="{{ url_for('product_list') }}" class="button-link">商品一覧に戻る</a></li>
<li><a href="{{ url_for('manage_products') }}" class="button-link">商品管理ページ</a></li>
</ul>
<h1>商品追加フォーム</h1>
<form method="POST" action="{{ url_for('add_product') }}">
{{ form.hidden_tag() }} <!-- CSRFトークンを含めるため -->
<label for="product_name">商品名</label>
{{ form.product_name() }} <!-- 商品名入力フィールド -->
{% if form.product_name.errors %}
<ul>
{% for error in form.product_name.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<label for="product_price">価格</label>
{{ form.product_price() }} <!-- 価格入力フィールド -->
{% if form.product_price.errors %}
<ul>
{% for error in form.product_price.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<label for="category">カテゴリー</label>
{{ form.category() }}
{% if form.category.errors %}
<ul>
{% for error in form.category.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<input type="submit" value="商品を登録する">
</form>
</body>
</html>
products.htmlの修正
products.htmlの修正後の内容。編集ボタンと削除ボタンをformタグを利用して表示しています。「class=”product-item”」「class=”product-name”」「class=”edit-button”」「class=”delete-button”」を該当するタグに追加しています。
<!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='css/style.css') }}">
</head>
<body>
<h1>商品一覧ページ</h1>
<ul>
<li><a href="{{ url_for('add_product') }}" class="button-link">商品を追加する</a></li>
<li><a href="{{ url_for('manage_products') }}" class="button-link">商品管理ページ</a></li>
</ul>
<h1>商品一覧</h1>
{% for category, products in grouped_products.items() %}
<h2>カテゴリー: {{ category }}</h2>
<ul class="product-list">
{% for product in products %}
<li class="product-item">
<span class="product-name">{{ product.name }} : ¥{{ "{:,}".format(product.price) }}</span>
<div class="button-group">
<form action="{{ url_for('edit_product', product_id=product.id) }}" method="GET"
style="display:inline;">
<button type="submit" class="edit-button">編集</button>
</form>
<form action="{{ url_for('delete_product', product_id=product.id) }}" method="POST"
style="display:inline;">
<button type="submit" class="delete-button"
onclick="return confirm('本当に削除しますか?※既に注文で利用されている場合は削除できません。')">削除</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
スタイルシートへのセレクタ追加
style.css
body {
font-family: Arial, sans-serif;
margin: 20px;
padding: 20px;
background-color: #f8f8f8;
}
h1 {
color: #333;
}
/* ボタン部分のリストを横並びにする */
ul {
padding: 0;
}
ul:first-of-type {
display: flex;
/* 横並び */
justify-content: flex-start;
/* 左寄せ */
}
ul:first-of-type li {
margin-right: 10px;
/* ボタン間に余白 */
}
/* リストアイテムの基本スタイル */
li {
list-style-type: none;
/* デフォルトのリストスタイルを削除 */
}
/* カテゴリーごとの商品リストのスタイル */
ul.product-list {
list-style-type: none;
padding: 0;
}
ul.product-list li {
background-color: #fff;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* ボタンリンクのスタイル */
.button-link {
display: block;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 16px;
transition: background-color 0.3s ease;
}
/* ボタンにホバーしたときのスタイル */
.button-link:hover {
background-color: #45a049;
}
/* 商品リストのスタイル */
h2 {
margin-top: 30px;
/* 商品リストの見出しに余白 */
}
ul:nth-of-type(2) {
list-style-type: none;
/* 商品リストのマーカーを削除 */
padding: 0;
}
ul:nth-of-type(2) li {
background-color: #fff;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* フォーム全体のスタイル */
form {
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-top: 30px;
}
/* フォーム内のラベルのスタイル */
form label {
font-size: 16px;
color: #333;
display: block;
margin-bottom: 8px;
}
/* フォーム内の入力フィールドのスタイル */
form input[type="text"],
form input[type="number"],
form input[type="submit"] {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
box-sizing: border-box;
/* パディングを含めてサイズ調整 */
}
/* フォーム内のボタン(送信ボタン) */
form input[type="submit"] {
background-color: #4CAF50;
color: white;
cursor: pointer;
transition: background-color 0.3s ease;
}
/* 送信ボタンにホバーしたときのスタイル */
form input[type="submit"]:hover,
form button:hover {
background-color: #45a049;
}
/* カテゴリーの選択ボックス(select) */
form select {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
box-sizing: border-box;
transition: border-color 0.3s ease;
}
/* フォーカス時のスタイル */
form select:focus {
border-color: #4CAF50;
outline: none;
box-shadow: 0 0 5px rgba(76, 175, 80, 0.5);
}
/* リストアイテムを横並びにする */
.category-item,
.product-item {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* カテゴリー名、商品名のスタイル */
.category-name,
.product-name {
font-size: 16px;
flex-grow: 1;
/* 左側に余白を作り、ボタンを右寄せ */
}
/* ボタンを右側に配置 */
.button-group {
display: flex;
gap: 10px;
/* ボタン間のスペース */
}
/* ボタンを囲む form のスタイルをリセット */
.button-group form {
display: inline-block;
margin: 0;
padding: 0;
}
/* 編集・削除ボタンの共通スタイル */
.button-group button {
display: inline-block;
padding: 8px 16px;
font-size: 14px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
color: white;
width: 80px;
/* 幅を統一 */
text-align: center;
}
/* 編集ボタンのスタイル */
.button-group .edit-button {
background-color: #2196F3;
/* ブルー */
}
.button-group .edit-button:hover {
background-color: #1976D2;
/* ホバー時の色 */
}
/* 削除ボタンのスタイル */
.button-group .delete-button {
background-color: #F44336;
/* レッド */
}
.button-group .delete-button:hover {
background-color: #D32F2F;
/* ホバー時の色 */
}
追加する内容は次のセレクタ名です。「.product-item」「.product-name」を追加しました、カンマ区切りで記述することで「.category-item」や「.category-name」と同様のスタイルを適用できます。
/* リストアイテムを横並びにする */
.category-item,
.product-item {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* カテゴリー名、商品名のスタイル */
.category-name,
.product-name {
font-size: 16px;
flex-grow: 1;
/* 左側に余白を作り、ボタンを右寄せ */
}
ブラウザで http://127.0.0.1:5000/manage_productsにアクセスします。
商品管理ページが表示されます。

商品一覧ボタンをクリックすると、商品項目の右側に編集ボタンと削除ボタンが表示されます。

グレープジュースの編集ボタンをクリックします。「編集」ボタンをクリックするとhttp://127.0.0.1:5000/product/edit/3?に移動します。

商品名に「コーラ」、価格に「350」カテゴリーに「アルコール」と入力して「商品を登録する」ボタンをクリックします。

http://127.0.0.1:5000/producs にリダイレクトされます。商品データ「コーラ」の「削除」ボタンをクリックします。

「削除」ボタンをクリックするとメッセージが表示されます。「OK」をクリックします。

「OK」をクリックするとコーラが削除されます。

今回は以上になります。

ブックマークのすすめ
「ほわほわぶろぐ」を常に検索するのが面倒だという方はブックマークをお勧めします。ブックマークの設定は別記事にて掲載しています。

