
目標
- 商品編集時に以前の商品名や価格をテキストボックスへ表示させる
- アプリケーション内の画像を選択できるようにする
- 商品に「販売中」「完売」「在庫切れ」「販売中止」ステータスを作成して「完売」「在庫切れ」は選択不可とし、「販売中止」は表示しない
商品管理アプリケーションの修正
商品管理アプリケーション修正
ここからは、これまで、商品管理アプリケーションで不便と感じていた部分について修正を行います。具体的には次の通りです。
- 商品一覧の編集ボタンをクリックしたときに、表示される編集画面で、編集前の商品名、価格、カテゴリーを初期値でセットさせます。
(Git では release-products-001 ブランチで修正) - 商品編集で画像をアップロードする以外に、既にアップロード済みの画像からも画像を選択できるようにします。
(Git では release-products-002 ブランチで修正) - 商品に「販売中」「完売」「在庫切れ」「販売中止」ステータスを作成して「完売」「在庫切れ」は選択不可とし、「販売中止」は表示しないようにします。
(Git では release-products-003 ブランチで修正)
release-productsブランチの作成
release-productsブランチの作成
商品管理アプリケーションは既に基本となる構成については作成を完了しています。このように、機能の追加漏れがあるような場合は、再度、featureブランチを作成するよりも、releaseブランチとしてブランチを作成して作業を行うのが一般的です。
Git Bushを立ち上げるとプロンプトの右側に(develop)と表示され、現在のブランチがdevelopブランチであることが分かります。(次の表示はVisual Studio CodeのGit Bushのプロンプトです。)
User@dellDevice MINGW64 ~/Desktop/FlaskProj (develop)
次のようにコマンドを入力していきます。次のように表示されるはずです。
$ git status

$ git checkout -b release-products-002

$ git status

商品管理アプリケーションの修正(release-products-002)
app.pyの編集(productsapp内)
app.pyファイルを次のように編集します。
import os
from itertools import groupby
from flask import (
Blueprint,
current_app,
flash,
jsonify,
redirect,
render_template,
request,
url_for,
)
from werkzeug.utils import secure_filename
from apps.common.db import db
from apps.productsapp.forms import AddProductForm, CategoryForm
from apps.productsapp.models import Category, Product # models.pyからインポート
products_bp = Blueprint(
"products",
__name__,
template_folder=os.path.join(os.getcwd(), "apps", "productsapp", "templates"),
static_folder=os.path.join(os.getcwd(), "apps", "productsapp", "static"),
)
def create_products_app(app):
"""Blueprintを新規作成し、登録する関数"""
return products_bp # 返り値としてBlueprintを返す
# アップロードが許可されている拡張子かをチェック
def allowed_file(filename):
return (
"." in filename
and filename.rsplit(".", 1)[ 1 ].lower()
in current_app.config["ALLOWED_EXTENSIONS"]
)
# ルート: トップページ
@products_bp.route("/")
def index():
return "Top page"
# カテゴリー一覧を表示
@products_bp.route("/categories")
def category_list():
categories = Category.query.all()
return render_template(
"categories.html", categories=categories, title="カテゴリー一覧"
)
# カテゴリーを追加
@products_bp.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("products.category_list"))
return render_template(
"category_form.html", form=form, title="カテゴリー追加フォーム"
)
# カテゴリーを編集
@products_bp.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("products.category_list"))
return render_template("category_form.html", form=form, title="カテゴリー編集")
# カテゴリーを削除
@products_bp.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("products.category_list"))
db.session.delete(category)
db.session.commit()
flash("カテゴリーを削除しました", "success")
return redirect(url_for("products.category_list"))
# ルート: 商品一覧ページ
@products_bp.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, title="商品一覧"
)
# ルート: 商品追加ページ
@products_bp.route("/product/add", 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
# 画像のアップロード処理
if "product_image" in request.files:
image = request.files.get("product_image")
if image and allowed_file(image.filename):
filename = secure_filename(image.filename) # ファイル名を安全に処理
image_path = os.path.join(current_app.config["UPLOAD_FOLDER"], filename)
image.save(image_path) # 画像を保存
# 画像のパスをデータベースに保存
image_url = f"images/{filename}" # アップロードされた画像のURL
else:
image_url = "images/noimage.png"
else:
image_url = "images/noimage.png"
# 商品の追加
new_product = Product(
name=name, price=price, category_id=category_id, image_url=image_url
)
db.session.add(new_product)
db.session.commit()
return redirect(url_for("products.product_list"))
return render_template("product_form.html", form=form, title="商品追加フォーム")
# ルート: 商品を編集
@products_bp.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()
# 編集の場合、該当するカテゴリーをデフォルトで選択
form.category.data = product.category_id
if form.validate_on_submit():
product.name = form.product_name.data
product.price = form.product_price.data
product.category_id = form.category.data
# 画像のアップロード処理
if "product_image" in request.files:
image = request.files["product_image"]
if image and allowed_file(image.filename):
filename = secure_filename(image.filename)
image_path = os.path.join(current_app.config["UPLOAD_FOLDER"], filename)
# もしすでに画像があれば削除
if product.image_url and product.image_url != "images/noimage.png":
old_image_path = os.path.join(
current_app.config["UPLOAD_FOLDER"],
os.path.basename(product.image_url),
)
if os.path.exists(old_image_path):
os.remove(old_image_path)
# 新しい画像を保存
image.save(image_path)
product.image_url = f"images/{filename}" # 新しい画像URLを保存
# 既存画像の選択
elif request.form.get("selected_image"):
product.image_url = request.form["selected_image"]
db.session.commit()
flash("商品情報を更新しました", "success")
return redirect(url_for("products.product_list"))
return render_template(
"product_form.html", form=form, title="商品編集", product=product
)
# ルート: 商品を削除
@products_bp.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("products.product_list"))
# ルート: 商品管理ページ
@products_bp.route("/manage_products")
def manage_products():
return render_template("manage_products.html", title="商品管理MENU")
@products_bp.route("/product/images")
def list_product_images():
"""保存されている商品画像の一覧を取得"""
image_folder = current_app.config["UPLOAD_FOLDER"]
images = [
filename for filename in os.listdir(image_folder) if allowed_file(filename)
]
return jsonify({"images": images})
if __name__ == "__main__":
from flask import Flask
app = Flask(__name__)
app.register_blueprint(products_bp, url_prefix="/products")
app.run(debug=True)
@products_bp.route("/product/images")
def list_product_images():
"""保存されている商品画像の一覧を取得"""
image_folder = current_app.config["UPLOAD_FOLDER"]
images = [
filename for filename in os.listdir(image_folder) if allowed_file(filename)
]
return jsonify({"images": images})
このプログラムは、Flaskで定義されたエンドポイント/product/imagesにアクセスしたときに、保存されている商品画像の一覧を取得してJSON形式で返す処理を行っています。
current_app.config[“UPLOAD_FOLDER”] はFlaskアプリケーションの設定からUPLOAD_FOLDERというキーの設定値を取得しています。ここで指定されているのは、商品画像が保存されているディレクトリのパスです。(main.pyに記述)
current_appは現在のFlaskアプリケーションインスタンスを指し、configはアプリケーション設定を格納するオブジェクトです。
os.listdir(image_folder)は、指定されたディレクトリ(image_folder)内のファイル名の一覧を取得します。このメソッドは、ディレクトリ内のすべてのファイルとフォルダをリストとして返します。
allowed_file(filename) は、ファイル名が指定された許可された拡張子(例えば .jpg, .png など)であるかを確認するための関数です。allowed_file関数がTrueを返す場合、そのファイルは許可された画像ファイルであると見なされ、imagesリストに追加されます。
このリスト内包表記により、許可された画像ファイルのみがimagesリストに格納されます。
jsonify({“images”: images})は、PythonのリストimagesをJSON形式に変換してHTTPレスポンスとして返します。jsonifyはFlaskでJSONレスポンスを返すために使う便利な関数です。
このコードは、{“images”: [“image1.jpg”, “image2.png”, …]}という形式で、クライアントに商品画像の一覧をJSONで返します。
product_form.htmlの編集(productsapp/templates内)
product_form.html を次のように編集します。
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block header %}
<ul>
<li><a href="{{ url_for('products.manage_products') }}" class="button-link">商品管理ページ</a></li>
<li><a href="{{ url_for('products.product_list') }}" class="button-link">商品一覧に戻る</a></li>
</ul>
{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method="POST"
action="{{ url_for('products.edit_product', product_id=product.id) if product else url_for('products.add_product') }}"
enctype="multipart/form-data">
{{ form.hidden_tag() }} <!-- CSRFトークンを含めるため -->
<label for="product_name">商品名</label>
{{ form.product_name(value=product.name if product else '') }} <!-- 商品名入力フィールド -->
{% 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(value=product.price if product else '') }} <!-- 価格入力フィールド -->
{% 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 %}
<label for="product_image">商品画像</label>
<!-- 既存の画像がある場合は表示 -->
{% if product and product.image_url %}
<div>
<img id="preview-image" src="{{ url_for('products.static', filename=product.image_url) }}" alt="現在の画像"
style="width: 100px; height: 100px;">
<p>※新しい画像を選択すると上書きされます</p>
<p id="upload-file-name"></p>
</div>
{% else %}
<img id="preview-image" src="" alt="プレビュー" style="display: none; width: 100px; height: 100px;">
{% endif %}
<label for="product_image" class="file-upload-label">画像のアップロード</label>
<input type="file" id="product_image" name="product_image" accept="image/*">
<!-- 画像選択ボタン -->
<label for="selected-image" id="select-image-btn">アプリ内画像から選択</label>
<input type="hidden" id="selected-image" name="selected_image" value="{{ product.image_url if product else '' }}">
<!-- 画像選択モーダル(初期状態は非表示) -->
<!-- 画像選択モーダル -->
<div id="image-modal" class="modal" style="display: none;">
<div class="modal-content">
<span id="close-modal" class="close">×</span>
<h3>画像を選択</h3>
<div id="image-list"></div>
</div>
</div>
<input type="submit" value="商品を登録する">
</form>
<script>
document.getElementById("product_image").addEventListener("change", function () {
const file = this.files[0];
const fileNameDisplay = document.getElementById("upload-file-name");
if (file) {
fileNameDisplay.textContent = file.name;
// 画像プレビューを表示
const reader = new FileReader();
reader.onload = function (e) {
const preview = document.getElementById("preview-image");
preview.src = e.target.result;
preview.style.display = "block";
};
reader.readAsDataURL(file);
}
});
document.addEventListener("DOMContentLoaded", function () {
fetch("{{ url_for('products.list_product_images') }}")
.then(response => response.json())
.then(data => {
const imageList = document.getElementById("image-list");
imageList.innerHTML = "";
data.images.forEach(image => {
const imgElement = document.createElement("img");
imgElement.src = "{{ url_for('products.static', filename='images/') }}" + image;
imgElement.classList.add("selectable-image");
imgElement.style.width = "100px";
imgElement.style.height = "100px";
imgElement.style.cursor = "pointer";
imgElement.dataset.image = "images/" + image;
imgElement.addEventListener("click", function () {
document.getElementById("selected-image").value = this.dataset.image;
document.getElementById("preview-image").src = this.src;
document.getElementById("preview-image").style.display = "block";
document.getElementById("image-modal").style.display = "none";
});
imageList.appendChild(imgElement);
});
})
.catch(error => console.error("画像取得エラー:", error)); // エラーハンドリング
});
document.getElementById("select-image-btn").addEventListener("click", function () {
document.getElementById("image-modal").style.display = "block";
});
document.getElementById("close-modal").addEventListener("click", function () {
document.getElementById("image-modal").style.display = "none";
});
</script>
{% endblock %}
ここで追加したプログラムは、商品画像を選択するためのインタラクティブなユーザーインターフェースを提供するものです。ユーザーが「アプリ内画像から選択」ボタンをクリックすると、モーダルウィンドウが表示され、商品画像を選んで登録できるようになります。
<label for="selected-image" id="select-image-btn">アプリ内画像から選択</label>
<input type="hidden" id="selected-image" name="selected_image" value="{{ product.image_url if product else '' }}">
<label>タグは「アプリ内画像から選択」ボタンを表示します。このラベルがクリックされると、id=”select-image-btn”がトリガーとなり、画像選択モーダルが表示されます。
<input type=”hidden”>は隠しフィールドです。選択された画像のパスがselected_imageという名前でフォームに送信されるように設定されています。
<div id="image-modal" class="modal" style="display: none;">
<div class="modal-content">
<span id="close-modal" class="close">×</span>
<h3>画像を選択</h3>
<div id="image-list"></div>
</div>
</div>
これは画像選択用のモーダル(ポップアップウィンドウ)です。style=”display: none;”によって初期状態では非表示です。id=”image-modal”でモーダル全体を、id=”image-list”で選択可能な画像リストを表示するための場所を指定しています。
spanタグにあるid=”close-modal”は、モーダルを閉じるための「×」ボタンです。
document.getElementById("product_image").addEventListener("change", function () {
const file = this.files[0];
const fileNameDisplay = document.getElementById("upload-file-name");
if (file) {
fileNameDisplay.textContent = file.name;
// 画像プレビューを表示
const reader = new FileReader();
reader.onload = function (e) {
const preview = document.getElementById("preview-image");
preview.src = e.target.result;
preview.style.display = "block";
};
reader.readAsDataURL(file);
}
});
この部分は、商品画像が選択されたときに、プレビューを表示する処理です。#product_image(ファイル選択フィールド)に画像が選択されると、FileReaderを使ってその画像をプレビュー表示します。
画像が選択されると、その画像のファイル名が表示され、画像のプレビューが画面に表示されます。
document.addEventListener("DOMContentLoaded", function () {
fetch("{{ url_for('products.list_product_images') }}")
.then(response => response.json())
.then(data => {
const imageList = document.getElementById("image-list");
imageList.innerHTML = "";
data.images.forEach(image => {
const imgElement = document.createElement("img");
imgElement.src = "{{ url_for('products.static', filename='images/') }}" + image;
imgElement.classList.add("selectable-image");
imgElement.style.width = "100px";
imgElement.style.height = "100px";
imgElement.style.cursor = "pointer";
imgElement.dataset.image = "images/" + image;
imgElement.addEventListener("click", function () {
document.getElementById("selected-image").value = this.dataset.image;
document.getElementById("preview-image").src = this.src;
document.getElementById("preview-image").style.display = "block";
document.getElementById("image-modal").style.display = "none";
});
imageList.appendChild(imgElement);
});
})
.catch(error => console.error("画像取得エラー:", error));
});
ページがロードされると、fetchを使ってサーバーから画像の一覧を取得します。URL {{ url_for(‘products.list_product_images’) }} は、サーバー側で設定された画像一覧を返すエンドポイントです。
サーバーから返された画像リスト(data.images)を基に、<img>タグを動的に作成し、#image-list内に表示します。
各画像にはクリックイベントが追加されており、クリックするとその画像が選択され、隠し入力フィールド(#selected-image)に画像のパスが設定され、プレビューが更新されます。
モーダルは画像が選択された時に非表示になります。
document.getElementById("select-image-btn").addEventListener("click", function () {
document.getElementById("image-modal").style.display = "block";
});
document.getElementById("close-modal").addEventListener("click", function () {
document.getElementById("image-modal").style.display = "none";
});
#select-image-btnがクリックされると、画像選択モーダル(#image-modal)が表示されます。
モーダル内の#close-modal(×ボタン)がクリックされると、モーダルが非表示になります。
style.cssの編集(productsapp/static/css内)
style.cssを次のように編集します。
body {
font-family: Arial, sans-serif;
margin: 20px;
padding: 20px;
background-color: #f8f8f8;
}
header {
margin: 0;
padding: 10px 0;
}
main {
margin: 0;
padding: 20px;
}
footer {
background-color: #aaa;
color: white;
text-align: center;
padding: 10px 0;
margin-top: 20px;
}
h1 {
color: #333;
}
/* ボタン部分のリストを横並びにする */
header ul,
main ul {
padding: 0;
}
header ul:first-of-type {
display: flex;
/* 横並び */
justify-content: flex-start;
/* 左寄せ */
}
header ul:first-of-type li {
margin-right: 10px;
/* ボタン間に余白 */
}
/* リストアイテムの基本スタイル */
header 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;
}
/* ファイル選択ボタンを隠す */
input[type="file"] {
display: none;
}
/* カスタムアップロードボタンのスタイル */
.file-upload-label {
display: inline-block;
padding: 10px 20px;
background-color: #f2491f;
color: white;
text-align: center;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
}
/* ホバー時のスタイル */
.file-upload-label: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;
/* ホバー時の色 */
}
/* 画像の間に隙間を追加 */
#image-list img {
margin: 10px;
/* 画像同士の間隔を10pxに設定 */
}
/* 画像選択ボタンのスタイル */
#select-image-btn {
display: inline-block;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 16px;
transition: background-color 0.3s ease;
outline: none;
}
/* ホバー時のスタイル */
button:focus,
#select-image-btn:hover {
background-color: #45a049;
outline: none;
}
http://127.0.0.1:5000/products/manage_productsにアクセスし、商品一覧をクリックして次のページを表示します。

りんごジュースの「編集」をクリックします。

「アプリない画像から選択」をクリックします。これまでアップロードされた画像が表示されます。

右の画像をクリックします。選択画像が更新されます。

「商品を登録する」をクリックします。リダイレクトされたページで、りんごジュースの画像が変更されたのが確認できます。

再度、編集ボタンをクリックして、画像を以前のものに戻します。

「商品を登録する」をクリックします。

コミット作業とrelease-productsブランチの削除
作業が完了したので「release-products-002」ブランチを「develop」ブランチにマジして、「release-products-002」ブランチは削除していきます。
git statusコマンドでファイルの状態を確認します。

git add .コマンドを入力します。

再度、git statusコマンドを入力します。

git commitコマンドを入力します。

Fix:商品管理アプリケーションの修正-002(20250324)
今回は設計書などはないため、設計書番号などの提示はなし。商品編集で画像をアップロードする以外に、既にアップロード済みの画像からも画像を選択できるように修正。

上書き保存をして「COMMIT_EDITMSG」ファイルを閉じるとコミットが完了します。

developブランチへのマージ
コミットしたrelease-products-002ブランチをdevelopブランチへマージします。
git checkout developコマンドで、ブランチをdevelopブランチへ切り替えます。

git merge release-products-002コマンドで release-products-002ブランチの内容をdevelopブランチへマージします。

release-products-002ブランチの削除
developブランチへのマージが完了したら、release-products-002ブランチを削除します。
git branch -d release-products-002コマンドでrelease-products-002ブランチを削除します。

今回は以上になります。

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

