python| まとめ | 現役エンジニア&プログラミングスクール講師「python」のまとめページです。pythonに関して抑えておきたい知識や文法やにについて記事をまとめています。まとめページの下部には「おすすめの学習書籍」「おすすめのITスクール情報」「おすすめ求人サイト」について情報を掲載中...

目標

  • 見た目を統一して、システムのユーザービリティを高めることができる。
  • render_template() と {{ title }} を利用した値の呼び出しができる。
  • Ruleの変更(ルートのURLルールを変更)を行える。
  • フォームクラスでSubmitFieldを利用できる。

Flaskアプリケーションのレイアウト

見た目を統一して、システムのユーザービリティを高める

前回までで作成したアプリケーションでは、ページごとのボタンの配置に統一感がなく、ユーザーの操作に混乱が生じる可能性がありました。具体的には商品一覧ページでは「商品を追加する」ボタンが左にあり、「商品管理ページ」ボタンが右にあるのに対して、カテゴリー一覧ページでは「カテゴリーを追加」ボタンが右にあり、「商品管理ページ」ボタンが左にありました。

また、「商品を追加する」と「カテゴリーを追加」のように、文言も不均一でしたし、各ページの「〇〇ページ(例:商品一覧ページ)」といったタイトルが、ブラウザのタブに表示されるタイトルや、すぐ下に表示される「商品一覧」と重複していることから不必要な表示であることも考える必要があります。

この記事では、それらに一定のルールを設けて、見た目を統一していきます。

manage_products.html

ブラウザの<title>タグ部分は見出し<h1>タグ部分と揃えます(商品管理MENU)。この部分はapp.pyにある render_template() のtitle引数に指定し、テンプレート側で{{ title }}で読み込みます。

app.py
# ルート: 商品管理ページ
@app.route("/manage_products")
def manage_products():
    return render_template("manage_products.html", title="商品管理MENU")
manage_products.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

<body>
    <h1>{{ title }}</h1>
    <ul>
        <li><a href="{{ url_for('product_list') }}" class="button-link">商品一覧</a></li>
        <li><a href="{{ url_for('add_product') }}" class="button-link">商品追加</a></li>
        <li><a href="{{ url_for('category_list') }}" class="button-link">カテゴリ一覧</a></li>
        <li><a href="{{ url_for('add_category') }}" class="button-link">カテゴリ追加</a></li>
    </ul>
</body>

</html>

変更後の表示

products.html

ブラウザの<title>タグ部分は見出し<h1>タグ部分と揃えます(商品一覧)。この部分はapp.pyにある render_template() のtitle引数に指定し、テンプレート側で{{ title }}で読み込みます。

app.py
# ルート: 商品一覧ページ
@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, title="商品一覧"
    )
products.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

<body>
    <ul>
        <li><a href="{{ url_for('manage_products') }}" class="button-link">商品管理ページ</a></li>
        <li><a href="{{ url_for('add_product') }}" class="button-link">商品を追加する</a></li>
    </ul>

    <h1>{{ title }}</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>

変更後の表示

products_form.html

ブラウザの<title>タグ部分は見出し<h1>タグ部分と揃えます(商品追加フォーム)。この部分はapp.pyにある render_template() のtitle引数に指定し、テンプレート側で{{ title }}で読み込みます。

また、商品編集の場合は商品リストから編集ボタンをクリックすることで編集用のフォームが表示されます。この部分も「商品追加フォーム」とあわせる形で「商品編集フォーム」にします。これはapp.pyファイルのedit_product関数にある render_template() のtitle引数を編集するだけです。

少し細かいですが、カテゴリーを追加する場合のURLは/category/addとなっています。このことから、商品追加の場合もURLを/product/addとして統一していきます。この編集は@app.route()の第一引数を書き換えることで行えます。※RuleやEndpointについてはflask routesコマンドで確認できます。

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("product_form.html", form=form, title="商品追加フォーム")


# ルート: 商品を編集
@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="商品編集フォーム")
product_form.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

<body>

    <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>{{ title }}</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>

変更後の表示

categories.html

ブラウザの<title>タグ部分は見出し<h1>タグ部分と揃えます(カテゴリー一覧)。この部分はapp.pyにある render_template() のtitle引数に指定し、テンプレート側で{{ title }}で読み込みます。

app.py
# カテゴリー一覧を表示
@app.route("/categories")
def category_list():
    categories = Category.query.all()
    return render_template("categories.html", categories=categories, title="カテゴリー一覧")
categories.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

<body>
    <ul>
        <li><a href="{{ url_for('manage_products') }}" class="button-link">商品管理ページ</a></li>
        <li><a href="{{ url_for('add_category') }}" class="button-link">カテゴリーを追加</a></li>
    </ul>
    <h1>{{ title }}</h1>
    <ul>
        {% for category in categories %}
        <li class="category-item">
            <span class="category-name">{{ category.name }}</span>
            <div class="button-group">
                <form action="{{ url_for('edit_category', category_id=category.id) }}" method="GET">
                    <button type="submit" class="edit-button">編集</button>
                </form>
                <form action="{{ url_for('delete_category', category_id=category.id) }}" method="POST">
                    <button type="submit" class="delete-button"
                        onclick="return confirm('本当に削除しますか?※既に商品が存在する場合は削除できません。')">削除</button>
                </form>
            </div>
        </li>
        {% endfor %}
    </ul>
</body>

</html>

変更後の表示

category_form.html

ブラウザの<title>タグ部分は見出し<h1>タグ部分と揃えます(カテゴリー追加フォーム)。この部分はapp.pyにある render_template() のtitle引数に指定し、テンプレート側で{{ title }}で読み込みます。

送信ボタン部分の文言はforms.pyのCategoryFormクラスにあるSubmitFieldで指定します。今回は「カテゴリーを追加」としています。

app.py
# カテゴリーを追加
@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="カテゴリー追加フォーム"
    )
forms.py
class CategoryForm(FlaskForm):
    name = StringField("カテゴリー名", validators=[DataRequired()])
    submit = SubmitField("カテゴリーを追加")
category_form.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>

<body>
    <ul>
        <li><a href="{{ url_for('manage_products') }}" class="button-link">商品管理ページ</a></li>
        <li><a href="{{ url_for('category_list') }}" class="button-link">カテゴリー一覧ページ</a></li>
    </ul>
    <h1>{{ title }}</h1>
    <form method="POST">
        {{ form.hidden_tag() }}
        <label for="name">カテゴリー名</label>
        {{ form.name() }}
        {% for error in form.name.errors %}
        <p style="color: red;">{{ error }}</p>
        {% endfor %}
        {{ form.submit(class="button-link") }}
    </form>
</body>

</html>

変更後の表示

今回は以上になります。

「python」おすすめ書籍 ベスト3 | 現役エンジニア&プログラミングスクール講師「python」の学習でお勧めしたい書籍をご紹介しています。お勧めする理由としては、考え方、イメージなどを適切に捉えていること、「生のpython」に焦点をあてて解説をしている書籍であることなどが理由です。勿論、この他にも良い書籍はありますが、特に質の高かったものを選んで記事にしています。ページの下部には「おすすめのITスクール情報」「おすすめ求人サイト」について情報を掲載中。...

ブックマークのすすめ

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

「お気に入り」の登録・削除方法【Google Chrome / Microsoft Edge】「お気に入り」の登録・削除方法【Google Chrome / Microsoft Edge】について解説している記事です。削除方法も掲載しています。...
【パソコン選び】失敗しないための重要ポイント | 現役エンジニア&プログラミングスクール講師【パソコン選び】失敗しないための重要ポイントについての記事です。パソコンのタイプと購入時に検討すべき点・家電量販店で見かけるCPUの見方・購入者が必要とするメモリ容量・HDDとSSDについて・ディスプレイの種類・バッテリーの持ち時間や保証・Officeソフト・ウィルス対策ソフトについて書いています。...