11-Flask

Flask(Part.13)| 【ふたつのテーブルの利用(3)テンプレート部分の解説】

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

目標

  • Part.11で入力したテンプレート部分のプログラムを理解できる。

Flaskでデータベースを利用する方法

アプリケーションの修正ポイントを確認

Part.11で作成したアプリケーションはふたつのテーブルを利用するといったものでした。この時、ふたつのテーブルを利用するための注意点を記事の冒頭で確認しました。テーブルは次のように「カテゴリーテーブル」を作成し、「商品テーブル」の商品カテゴリーはこのカテゴリーテーブルを参照するというものでした。

アプリケーションで追加・修正したプログラムの解説

テンプレートとしては「categorys.html」「category_form.html」を新規で作成し、「add_product.html」「products.html」「manage_product.html」で追加と修正を行いました。

categorys.htmlの作成

categorys.htmlの内容。

<!DOCTYPE html>
<html lang="ja">

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

<body>
    <h1>カテゴリー一覧ページ</h1>
    <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>カテゴリー一覧</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>
{% for category in categories %}
  • categories という変数をループして、各カテゴリーを category という変数に代入します。
  • このループの中で各カテゴリーに対して、編集ボタンと削除ボタンを表示します。
<li class=”category-item”>
  • 各カテゴリーがリストアイテムとして表示されます。
  • class=”category-item” は、このリストアイテムに対してCSSでスタイルを適用するためのクラスです。
<span class=”category-name”>{{ category.name }}</span>
  • 各カテゴリーの名前(category.name)が表示されます。
  • {{ category.name }} はJinja2テンプレートエンジンの構文で、category.name というデータをHTMLに埋め込んでいます。
<div class=”button-group”>
  • 編集ボタンと削除ボタンがまとめて表示されるための div 要素です。クラス 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(‘edit_category’, category_id=category.id) }}”: url_for(‘edit_category’, category_id=category.id) は、edit_category というルート名に基づいて、カテゴリーIDをURLに含めたパスを生成します。これにより、カテゴリーIDに対応する編集ページへ遷移できます。
  • method=”GET”: GET メソッドを使って、編集ページを表示するリクエストを送信します。
  • 「編集」ボタンが表示され、クリックするとフォームが送信されます。
<form action="{{ url_for('delete_category', category_id=category.id) }}" method="POST">
    <button type="submit" class="delete-button" onclick="return confirm('本当に削除しますか?※既に商品が存在する場合は削除できません。')">削除</button>
</form>
  • form: 削除ボタンをクリックすると、このフォームが送信され、カテゴリーを削除するリクエストが送られます。
  • action=”{{ url_for(‘delete_category’, category_id=category.id) }}”: url_for(‘delete_category’, category_id=category.id) は、delete_category というルート名に基づいてカテゴリーIDをURLに含めたパスを生成します。これにより、カテゴリーIDに対応する削除処理が行われます。
  • method=”POST”: POST メソッドを使用して、削除リクエストを送信します。POST メソッドは、削除などの変更操作に使われます。
  • 「削除」ボタンが表示され、クリックするとフォームが送信されます。
  • onclick=”return confirm(‘本当に削除しますか?※既に商品が存在する場合は削除できません。’)”: ボタンがクリックされた時に、確認ダイアログが表示されます。ユーザーが「OK」をクリックした場合にのみフォームが送信され、削除処理が実行されます。
{% endfor %}
  • for ループを終了し、カテゴリーごとのリストアイテムの生成を終了します。

category_form.htmlの作成

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>
    <h1>カテゴリー追加ページ</h1>
    <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>
<h1>{{ title }}</h1>
  • {{ title }} は、FlaskのテンプレートエンジンJinja2を使って、title という変数をHTMLに埋め込んでいます。
  • title には、カテゴリーを追加または編集するページのタイトルが渡されることを想定しています。
<form method=”POST”>
  • フォームの送信メソッドを POST に指定しています。これにより、フォームのデータがサーバーに送信されるとき、POST メソッドが使用されます。
  • フォームが送信されると、FlaskサーバーはPOSTメソッドに対応した処理を実行します(例えば、カテゴリーの追加や更新)。
{{ form.hidden_tag() }}
  • form.hidden_tag() は、Flask-WTFフォームのセキュリティ用の隠しフィールド(CSRFトークン)を挿入します。
  • CSRFトークンは、クロスサイトリクエストフォージェリ(CSRF)攻撃からアプリケーションを保護するために使用されます。
<label for=”name”>カテゴリー名</label>
  • <label>タグは、name フィールドのラベル(説明文)を表示します。
  • for=”name” は、ラベルがどのフォーム要素に関連付けられるかを示しています。この場合、name フィールドに関連します。
{{ form.name() }}
  • form.name() は、CategoryForm クラスで定義された name フィールドをHTMLにレンダリングします。
  • ここでは、ユーザーがカテゴリー名を入力するための入力欄が生成されます。
  • Flask-WTFは、フォームのフィールドをHTMLの適切な要素にレンダリングするため、form.name() と書くと自動的に入力欄(<input>タグ)が生成されます。
{% for error in form.name.errors %}
    <p style="color: red;">{{ error }}</p>
{% endfor %}
  • form.name.errors には、name フィールドに関連するエラーメッセージがリストとして格納されます。
  • もしエラーがあれば、<p style="color: red;">{{ error }}</p> の形式で赤色でエラーメッセージが表示されます。
  • 例えば、name フィールドが空欄で送信されると、「このフィールドは必須です」といったエラーメッセージが表示されます。
{{ form.submit(class=”button-link”) }}
  • form.submit() は、フォームの送信ボタンを生成します。
  • class=”button-link” は、ボタンにCSSクラス button-link を追加して、スタイリングします。

add_product.htmlの修正

add_product.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='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>
カテゴリー選択部分を追加しています。
        <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=”category”>カテゴリー</label>
  • <label>タグは、フォームフィールドのラベルを表示します。この場合、カテゴリーを選択するフィールドに対して「カテゴリー」と表示されます。
  • for=”category” 属性は、ラベルが category フィールドに関連していることを示しています。これにより、ユーザーがラベルをクリックすると、カテゴリー選択のドロップダウンリストがフォーカスされます。
{{ form.category() }}
  • form.category() は、Flask-WTFで定義された category フィールドをHTMLにレンダリングします。
  • category フィールドは通常、SelectField というフォームフィールドタイプで定義されており、ユーザーにカテゴリーを選択させるためのドロップダウンリスト(<select>タグ)を生成します。例えば、SelectField にはカテゴリーのリスト(choices)が指定され、ユーザーがその中から1つを選ぶことができます。
エラーメッセージの表示
  • form.category.errors は、category フィールドに関連するバリデーションエラーのリストです。もしエラーがあれば、それらをリスト形式で表示します。
  • if form.category.errors によって、エラーがある場合のみエラーメッセージを表示するようになっています。
  • エラーが存在する場合、<ul>タグ内に各エラーメッセージが<li>タグとして表示されます。

products.htmlの修正

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>
    <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>{{ product.name }} : ¥{{ "{:,}".format(product.price) }}</li>
        {% endfor %}
    </ul>
    {% endfor %}

</body>

</html>
カテゴリーごとに表示するための修正を行っています。
    {% for category, products in grouped_products.items() %}
    <h2>カテゴリー: {{ category }}</h2>
    <ul class="product-list">
        {% for product in products %}
        <li>{{ product.name }} : ¥{{ "{:,}".format(product.price) }}</li>
        {% endfor %}
    </ul>
    {% endfor %}

最初のfor文でカテゴリーをひとつ選択し、内側のfor文でそのカテゴリーに該当する商品を表示します。

manage_products.htmlの修正

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>
    <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('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>

「商品一覧」「商品追加」「カテゴリー一覧」「カテゴリー追加」のよっつのボタンを表示しています。

スタイルシートの修正と追加

テンプレートで追加した項目や修正した項目に装飾を行っています。

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 {
    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 {
    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;
    /* ホバー時の色 */
}

今回は以上になります。

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

ブックマークのすすめ

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

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

Flask(Part.10)| 【アプリケーション名のリファクタリング】

2025年3月3日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
11-Flask

Flask(Part.12)| 【ふたつのテーブルの利用(2)ロジック部分の解説】

2025年3月5日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site