MacBookでのFlask開発環境セットアップ

手持ちのMacBookPro でFlask開発をする環境をセットアップするためのメモ

# 環境
- MacBookPro 13-inch 2020 Intel Core i7
- Flask 3.0.3
- pip 23.0.1

# 1.準備
## 1.1 treeコマンドインストール
 - MacBookに homebrewでtreeコマンドを入れる。時々階層表示で確認したいため
```bash
$ brew install tree
```

簡単な使い方
```bash:terminal
# 確認したい階層のフォルダに移動してから
tree
# 現在のディレクトリから2階層目まで表示
$ tree -L 2
# .env等も表示したい場合(-a)
$ tree -a
$ tree -aL 2
# ターミナル表示されたものをコピー
$ tree -L 2 | pbcopy
# テキストエディット開いて貼り付け
```

## 1.2.開発環境の階層を決める
- 例えばこんな感じ
```bash:terminal
# プロジェクトフォルダの階層に移動
cd pj05_test
# treeコマンド実行
# .DS_Storeなどは消してます
pj05_test% tree -a
.
├── .venv_pj05 # venv環境(pip listでの確認用)
|
├── flaskapp
│   ├── .env # 開発環境と本番環境で内容が異なる
│   │
│   ├── app.py
│   ├── views.py
│   │
│   │   # 以下は開発の必要に応じて追加していく
│   ├── generate_cy.py
│   ├── generate_elements_cy.py
│   ├── requirements.txt
│   │
│   ├── static
│   │   ├── css
│   │   │   ├── style.css #とりあえず１つ
│   │   │   └── 必要に応じて増やす
│   │   ├── csvdata # CSVを読み込んだものを表示したい場合
│   │   │   ├── dammy_data.csv
│   │   │   
│   │   ├── images #表示させたい適当な画像
│   │   │   ├── default_item.png
│   │   │   └── default_user.png
│   │   └── js
│   │       └── sample.js #jsをローカルに置きたい場合
│   │
│   ├── templates # 開発している中で増えていくが、動作確認終わったら消ししていく感じ
│   │   ├── index.html
│   │   ├── index2.html
│   │   ├── index3.html
│   │   ├── layout.html
│   │   ├── layout2.html
│   │   ├── layout3.html
│   │   ├── navbar_v1.html
│   │   ├── navbar_v2.html
│   │   └── navbar_v3.html
│   │
│   │  #開発環境では使わないけど
│   ├── uwsgi.ini
│   └── uwsgi.py
│
│   # 開発環境では使わないけど
└── web
    └── nginx.conf
```

## 1.3 venv
- venv環境を作る

```bash:pj05_test配下でvenv作成
#プロジェクトフォルダに移動
cd pj05_test
#venv環境作成
python -m venv .venv_pj05
# .venv_pj05は適当なvenv環境名
# .をつけて、Project通番もつけてる

# activate
source .venv_pj05/bin/activate

# 必要なものをインストール
pip list # 現状確認
# 必須なもの
pip install flask
pip install --upgrade pip
pip install python-dotenv
# 以降は、必要に応じてよしなに
pip install pandas
pip install openai
```

## 1.4 フォルダと空ファイルの作成
- 先にフォルダ作って、空ファイル作る。どこに何を置くかが明確なので

```bash:pj05_testフォルダ内で
# フォルダ作成
mkdir flaskapp
mkdir web
cd flaskapp
mkdir templates #sを忘れずに
mkdir static
# ファイル作成
touch app.py
touch views.py
touch .env
# そのまま書き込んでゆく場合
vi app.py
# この辺で素直にVisual Studio Codeを開く
```

## 1.5 .envに記述
- .envファイルを作成して以下を記述
```python:flaskapp/.env
#以下を記述
FLASK_APP=app.py        # <====開発環境のみ
FLASK_ENV=development   # <====開発環境のみ
# 必要に応じて
OPENAI_API_KEY = hogehogefugafuga
```

# 2.python記述
- VS Codeを開き、記述する
- 画面は、左にエクスプローラー、右上がコード、右下がターミナル表示にしてる


## 2.0 前提知識
- 私の前提知識がないところを、ChatGPTに聞いてみたので転機しておく
    - 疑問１：なぜFlaskは、``__init__.py``ではなく、``app.py``に記述するのか
    - 疑問２：app.pyとルーティング系views.pyに分けるメリット
- 生成AI(ChatGPTによる回答):
  - Flaskアプリケーションの開発で ``__init__.py`` ではなく ``app.py``  を使用するのは、主にプロジェクトのシンプルさや標準的な構成によるものです。また、ルーティング処理を `views.py` に分離するのは、コードの保守性や拡張性を高めるためです。それぞれの理由を詳しく説明します。

### 2.0.1. **`__init__.py` ではなく `app.py` を使う理由**

#### **2.0.1.1 プロジェクトのシンプルさ**
- 小規模なFlaskアプリケーションでは、1つのファイルにアプリケーションを記述することが一般的です
- `app.py` という名前は直感的であり、どのファイルがFlaskアプリケーションのエントリーポイントなのかが一目でわかります
- `__init__.py` は主に **パッケージ初期化** のためのファイルとして使われるため、アプリのエントリーポイントとするのはやや直感的ではありません

#### **2.0.1.2 エントリーポイントの明確化**
- Flaskアプリケーションを起動する際に、`flask run` や `python -m flask` で明示的にエントリーポイントを指定することがあります
- `app.py` であれば、Flaskはデフォルトで `app` というモジュール（`app.py`）をエントリーポイントとして探し、アプリケーションを起動します
    ```bash
    flask run --app app
    ```
- `__init__.py` にアプリを定義した場合、`--app` オプションでパッケージ名を指定する必要があり、やや冗長です

#### **2.0.1.3 モジュールとパッケージの違い**
- `app.py` は1つのモジュールとして機能し、単純なアプリケーションのエントリーポイントに適しています
- `__init__.py` はパッケージの初期化処理のために使われるため、プロジェクトが複数のモジュールから構成される大規模な場合に、Flaskアプリケーションのインスタンスをここで作成することが一般的です


### 2.0.2. **`app.py` と `views.py` を分離する理由**

#### **2.0.2.1 コードの責務を分離（Separation of Concerns）**
- `app.py` は **アプリケーションの設定** や **エントリーポイント** の役割を持たせ、`views.py` には **ルーティングやビジネスロジック** を記述することで責務が明確になります
- 例えば、`app.py` には以下のようにアプリケーションの設定のみを記述します
    ```python
    from flask import Flask
    app = Flask(__name__)
    
    # ルーティングのインポート
    from views import *
    
    if __name__ == "__main__":
        app.run(debug=True)
    ```
- `views.py` にはルーティング処理を記述します
    ```python
    from app import app
    @app.route("/")
    def home():
        return "Hello, Flask!"
    ```

#### **2.0.2.2 コードの保守性・拡張性**
- ルーティングが増えた場合、1つのファイルにすべて書くとコードが膨大になり、可読性が低下します。
- `views.py` を使って分割することで、異なる機能ごとにルーティング処理をファイル単位で整理できます。例えば：
    - `views.py`：基本ルーティング
    - `auth.py`：認証関連のルーティング
    - `api.py`：APIエンドポイントのルーティング
- 将来的に機能を追加・変更する際も、影響範囲が限定されるためメンテナンスが容易になります

#### **2.0.2.3 チーム開発のしやすさ**
- 大規模なアプリでは複数人での開発が必要になることが多いです。
- `app.py`、`views.py`、`auth.py` などに分離すれば、異なる担当者が異なる部分を同時に開発できます

#### **2.0.2.4 テストの容易さ**
- コードが分離されていることで、各部分をユニットテストしやすくなります
- 例えば、ルーティングやビジネスロジックのテストだけを `views.py` に対して行うことが可能です

### 2.0.3 まとめ
- **`app.py`** はFlaskアプリのエントリーポイントを明確にし、小規模なアプリケーションにはシンプルで適しています
- **`__init__.py`** はパッケージ構成が必要な大規模なアプリで使用されます
- ルーティングを `views.py` に分離することで、コードの責務が明確化され、保守性・拡張性・テストのしやすさが向上します

シンプルなアプリは `app.py` のみで完結できますが、開発が進むにつれてコードの分割やモジュール化が重要になってきます



## 2.1 app.py
- Flaskでは、通常app.pyがアプリインスタンスを定義する場所としている
- appモジュール(```フォルダ名や同名のapp.py```)を探しにいき、見つけたら
  それ(app.py)から(from)、そこに定義された appというオブジェクト 
  ```app=Flask(__name__)```ここではFlaskのアプリインスタンスをimportする
  
```python:flaskapp/app.py
#アプリケーションをインポート
from flask import Flask, render_template, request, jsonify
from dotenv import load_dotenv
import openai
import os
import sys
     
# Flaskアプリケーションのインスタンス作成
app = Flask(__name__)

# 同じ階層の views.pyを最後にインポートして循環インポートを回避
import views

# .envファイルの内容を読み込む
load_dotenv()

# OpenAI APIキー (必要に応じて)
#openai.api_key = os.environ.get('OPENAI_API_KEY')

# 以下views.pyに切り出す場合は不要
# @app.route('/')
# def index():
#   return '<h2>Hello World!</h2>'
      
# Pythonスクリプトとして実行された場合、アプリケーションを実行
# 開発環境のみで必要
if __name__ == "__main__":
    app.run(host='127.0.0.1', port=5000, debug=True) #開発環境のみで必要

```

## 2.2 views.py


```python:flaskapp/views.py
from app import app
from flask import render_template, request, jsonify, render_template_string
import openai

import generate_cy
import generate_elements_cy

# Flaskのルーティング
# 初手としてまずは動くか確認
@app.route('/')
def index():
    # step1
    return "<h1>Hellow World!</h1>"
    # step2
    #data = 'views.pyのinsert_something部分です'
    #return render_template("index.html", insert_something=data)
    # step3
    #my_dict_data = {
    #         'key1' : 'value1',
    #         'key2' : 'value2',
    #         'titles': [ 'title_name1', 'title_name2', 'title_name3']
    #         }
    #return render_template('index.html', my_dict=my_dict_data)

# index以外のページを作って動作確認
@app.route('/test')
def other1():
    return render_template('index2.html')

# layout.htmを使ったページでの動作確認
@app.route('/test3')
def test3():
    return render_template('index3.html')

# csvデータを読み込んで表示するページ
@app.route('/table')
def table():
    return render_template('dammy_table.html')

# pythonで吐き出したHTMLファイルをそのまま出す場合
@app.route('/graph')
def graph():
    return render_template('graph.html')


# jinja2等で生成したファイルをそのまま表示させる場合
# render_template_stringを使う。さくっと見たい場合
@app.route("/cy")
def network_graph():
    html_content = generate_cy.generate_html()
    return render_template_string(html_content)

# pythonでelements部分を作り、レイアウトに埋め込む場合
@app.route("/elements_cy")
def cytoscape_graph():
    elements = generate_elements_cy.generate_elements()
    return render_template("network_graph_layout.html", elements=elements)
```
## 2.3 その他Pythonファイル
- 必要に応じて適宜作成
- 共通して言えそうなところ
1. returnとしてHTMLファイルに埋め込んで返す場合

- ポイントは作成したPythonファイル名が ``generate_cy.py``としたとき、views.pyには、``from generate_cy``と読み込む行を忘れずに入れる
- 作成したpythonファイルの返り値がHTMLファイルでそのまま使える場合は、views.pyでは、``return render_template_string(html_content)``として入れる
 
```python:flaskapp/generate_cy.py
# 例1 HTMLファイルを返す場合
import pandas as pd
import networkx as nx
import json

def generate_html():
    # CSVファイルを読み込み
    df = pd.read_csv("./static/csvdata/dammy_data.csv")

    # NetworkXのグラフを作成
    G = nx.Graph()

　　# 中間コード省略

   # HTMLの生成
   html_content="""
   <!DOCTYPE html>
   <html lang="en">
   <head>
      <meta charset="UTF-8">
      <title>TITLE NAME</title>
      <link href="./static/css/xxx.css" rel="stylesheet" type="text/css" />
      <script src="./static/js/xxx.js"></script>
      <style>
            #cy {{
                width: 900px;
                height: 400px;
                border: 1px solid black;
            }}
     </style>
     </head>
     <body>
        <h2>Network Graph</h2>
        <div id="cy"></div>
        <hr>

        <script>
        <!-- 中略 -->
        </script>
    </body>
    </html>
    """
    return html_content
```

```python:flaskapp/views.py
from app import app
from flask import render_template, request, jsonify, render_template_string

import generate_cy #<--追加

@app.route('/')
def index():
   hogefuga

# jinja2等で生成したファイルをそのまま表示させる場合
# render_template_stringを使う。さくっと見たい場合
@app.route("/cy")
def network_graph():
    html_content = generate_cy.generate_html()
    return render_template_string(html_content)
```


2.HTMLファイル内の要素をreturnとして返す場合
- 作成したpythonファイルの返り値が、HTMLファイルを構成する要素(elements)だった場合は、それを埋め込むlayout系のHTMLファイルを用意し、views.pyには、``return render_template("出力HTML名.html", elements=elements)``とする

```python:flaskapp/generate_elements_cy.py
# 例2 elementsを返す場合
import pandas as pd
import networkx as nx
import json

def generate_elements():
    # csvファイルを読み込んでDFにする
    df = pd.read_csv("./static/csvdata/dammy_data.csv")

    # NetworkXのグラフを作成
    G = nx.Graph()
    
    #中間の処理は省略

    return elements
```

```html:flaskapp/templates/network_graph_layout.html
<!doctype html>
<html lang="en">
<head>
<!--中略-->
</head>
<body>
    <h2>Network Graph</h2>
    <div id="cy"></div>
    <script>
        var cy = cytoscape({
            container: document.getElementById('cy'),
            elements: {{ elements|tojson }},
            style: [
                {
        //中略
    </script>
</body>
</html>
```

```python:flaskapp/views.py
from app import app
from flask import render_template, request, jsonify, render_template_string

import generate_cy
import generate_elements_cy #<--忘れずに

# 中略

# pythonでelements部分を作り、レイアウトに埋め込む場合
@app.route("/elements_cy")
def cytoscape_graph():
    elements = generate_elements_cy.generate_elements()
    return render_template("network_graph_layout.html", elements=elements)
```




 
### render_templateと render_template_stringどちらを使うべきか？
- **render_template**:
    - 再利用性が高い
    - テンプレートファイルがコードと分離されているため、保守性が向上
    - 複雑なアプリケーションやプロダクション環境に最適
- **render_template_string**:
    - 簡単なプロトタイプやデバッグ用途に適している
    - HTMLテンプレートを外部ファイルとして管理したくない場合に便利
- 結論
    - プロジェクトの規模や要件に応じて選択
    - 長期的には、再利用性や保守性を考慮するとrender_templateに軍配



# 3.HTMLファイル記述
## 3.1 index.html
```html:templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>テストWebアプリ</title>
</head>
<body>
    <h1>index.html表示してます</h1>
    <!-- step2 　step2実行時は、step3は消しておく.コメントアウトはできない Internal Server Error-->
    {{ insert_something }}

    <!-- step3 -- step3実行時は Step2は、消して置かないとエラーになる-->
    <p>{{ my_dict.key1 }}</p>
    <p>{{ my_dict.key2 }}</p>

    <!-- /test (index2.html)へのルーティング def other1():の other1を指定している　-->
    <!-- views.pyでのルーティング設定、index2.htmlのファイルがない場合エラー -->
    <a href="{{ url_for('other1') }}">test</a>に移動する<br>

    <!-- /test3 (index3.html)へのルーティング def test3():の test3を指定している　---->
    <!-- views.pyでのルーティング設定、index3.htmlのファイルがない場合エラー -->
    <a href="{{ url_for('test3') }}">test3</a>に移動する<br>
</body>
</html>%                    
```

```html:templates/index2.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>テストWebアプリ2</title>
</head>
<body>
    <h1>index2.html表示してます</h1>
</body>
</html>
```

```html:index3.html
<!-- layout ~ layout3 は適宜選ぶ　-->
{% extends "layout.html" %}

{% block content %}
<h1>index3.html表示してます</h1>
<h2>タイトル表示</h2>
<p>このページはlayout.htmlを利用しています</p>

{% endblock %}
suzukita@szkMac templates % 
```

## 3.2 layout.html
- シンプルにlayoutを使った動作確認用
```html:templates/layout.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sample flask app</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
```
- BootstrapのNavbarだけ使った場合
```html:templates/layout2.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"/>

	<title> This is TEST WEB site</title>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd;">
        <div class="container-fluid">
          <a class="navbar-brand" href="#">Navbar</a>
          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
          </button>
          <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
            <div class="navbar-nav">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
              <a class="nav-link" href="#">Features</a>
              <a class="nav-link" href="#">Pricing</a>
              <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </div>
          </div>
        </div>
      </nav>
    {% block content %}
    {% endblock %}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

</body>
</html>
```
- DashboardをBootStrapで作り込みたいとき
- Navbarだけは、別ファイルにした場合

```html:flaskapp/templates/layout3.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap Sample</title>

    <!-- BootstrapのCDNでのCSS読み込み -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" 
          rel="stylesheet" 
          integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" 
          crossorigin="anonymous"
    >

    <!-- sidebar CSS --->
    <style>
        sidebar {
            position: fixed;
            top: 0;
            bottom: 0;
            left: 0;
            z-index: 100;
            padding: 90px 0 0;
            box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
            z-index: 99;
        }

        @media (max-width: 767.98px) {
        .sidebar {
            top: 11.5rem;
            padding: 0;
        }
        }

        .navbar {
        box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .1);
        }

        @media (min-width: 767.98px) {
        .navbar {
            top: 0;
            position: sticky;
            z-index: 999;
        }
        }

        .sidebar .nav-link {
        color: #333;
        }

        .sidebar .nav-link.active {
        color: #0d6efd;
        }
    </style>

</head>

<body>
    <!--NavBarの設定-->
    <!--includeで別ファイルに記述したテンプレートを読み込める-->
    {% include "navbar_v2.html" %}


    <!--Containerの設定-->
    <div class="container-fluid">
        <div class="row">
            <!-- sidebar content -->
            <nav id="sidebar" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
                <!-- sidebar content goes in here -->
                <div class="position-sticky pt-md-5">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                          <a class="nav-link active" aria-current="page" href="#">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
                            <span class="ml-2">Home</span>
                          </a>
                        </li>
                        <li class="nav-item">
                          <a class="nav-link" href="#">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
                            <span class="ml-2">Overview</span>
                          </a>
                        </li>
                        <li class="nav-item">
                          <a class="nav-link" href="#">
                            <i class="bi bi-database"></i>
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-database" viewBox="0 0 16 16">
                               <path d="M4.318 2.687C5.234 2.271 6.536 2 8 2s2.766.27 3.682.687C12.644 3.125 13 3.627 13 4c0 .374-.356.875-1.318 1.313C10.766 5.729 9.464 6 8 6s-2.766-.27-3.682-.687C3.356 4.875 3 4.373 3 4c0-.374.356-.875 1.318-1.313M13 5.698V7c0 .374-.356.875-1.318 1.313C10.766 8.729 9.464 9 8 9s-2.766-.27-3.682-.687C3.356 7.875 3 7.373 3 7V5.698c.271.202.58.378.904.525C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777A5 5 0 0 0 13 5.698M14 4c0-1.007-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1s-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4v9c0 1.007.875 1.755 1.904 2.223C4.978 15.71 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13zm-1 4.698V10c0 .374-.356.875-1.318 1.313C10.766 11.729 9.464 12 8 12s-2.766-.27-3.682-.687C3.356 10.875 3 10.373 3 10V8.698c.271.202.58.378.904.525C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777A5 5 0 0 0 13 8.698m0 3V13c0 .374-.356.875-1.318 1.313C10.766 14.729 9.464 15 8 15s-2.766-.27-3.682-.687C3.356 13.875 3 13.373 3 13v-1.302c.271.202.58.378.904.525C4.978 12.71 6.427 13 8 13s3.022-.289 4.096-.777c.324-.147.633-.323.904-.525"/>
                            </svg>
                            <span class="ml-2">DataTable</span>
                          </a>
                        </li>

                        <li class="nav-item">
                          <a class="nav-link" href="#">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg>
                            <span class="ml-2">Reports</span>
                          </a>
                        </li>

                      </ul>
                  </div>
                    
            </nav>

            <!-- main content -->
            <main class="col-md-9 ml-sm-auto col-lg-10 px-md-4 py-4">
                <!--パンくずリスト-->
                <nav aria-label="breadcrumb">
                    <ol class="breadcrumb">
                      <li class="breadcrumb-item"><a href="#">Home</a></li>
                      <li class="breadcrumb-item active" aria-current="page">Overview</li>
                    </ol>
                </nav>
                <h1 class="h2">Overview</h1>
                <p>This is the homepage of a simple admin interface which is part of a tutorial written on Themesberg</p>
                    

                <!--main content start-->
                {% block content %}{% endblock %}
                <!--main content end -->

                
                <!-- Footer は mainタグの終わりの前にいれる-->
                <footer class="pt-5 d-flex justify-content-between">
                    <span>Copyright © 2024-2025 <a href="">CORPORATION NAME</a></span>
                </footer>
                <!--Footerはmainタグが終わる前にいれる。ここまで-->
            </main>
        </div>
    </div>


    <!-- BootstrapのCDNでのjs読み込み -->
    <!--Bundle type: bootstrapに、popperをバンドルした一体型-->
    <script 
        src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
        crossorigin="anonymous"
    ></script>
    <!--Popperとbootstrap JSを別々も可能。ドロップダウン、ポップオーバー、ツールチップを使う予定がない場合は、Popperを含めないことで何キロバイトか節約することができる-->
    <!-- 分離型
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
    -->
</body>
</html>
```

#### Bootstrapのアイコンを変えたい場合は以下から選ぶ
https://icons.getbootstrap.jp/


## 3.3 Navbar
```html:flaskapp/templates/navbar_v2.html
<!--NavBarの設定-->
<nav class="navbar navbar-expand-lg bg-body-tertiary">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link active" aria-current="page" href="#">Home</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Features</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#">Pricing</a>
        </li>
        <li class="nav-item">
            <a class="nav-link disabled">Disabled</a>
        </li>
        </ul>
      </div>
    </div>
</nav> 
```

# 4.static系のものを揃える
## 4.1 css
- 必要なCSSを入れる
```css:flaskapp/static/css/style.css
h1 {
    color: blue;
    border-bottom: double 6px #87CEFA;
}
```
## 4.2 js
- jsに適当なjavascriptを入れる
```javascript:flaskapp/satic/js/sample.js 
window.onload = function() {
    var changeColor = function() {
        var e = document.getElementById('test');
        e.style.color = 'red';
        console.log("書き換えテスト")
    }
    setTimeout(changeColor, 5000);
}                   
```
## 4.3 iamges
- imagesに適当な画像を入れる
```text:flaskapp/static/images/sample.png
static
 │
 └── images
       │
       └──  sample.png
```


# 5.flaskを起動して確認

- flaskを起動する。開発中モードなので、起動時にWSGIを使ってねと警告がされるが、``http://127.0.0.1:5000``をcmd+クリックでブラウザ表示して確認する
- 確認が終了したら,CTRL+Cで止める

```bash:pj05_test/flaskapp
(.venv_pj05) username@Macbook flaskapp % flask run
 * Serving Flask app 'app.py'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
```

# 6.参考リンク

## Qiita  初心者プログラマーのWebアプリ
https://qiita.com/Bashi50/items/30065e8f54f7e8038323

## 【Docker】Nginx+uWSGI+Flaskのコンテナ構築
https://amateur-engineer-blog.com/nginx-uwsgi-flask-docker

## Flask Documentationの日本語訳
https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/index.html

## Bootstrap getting-started
https://getbootstrap.jp/docs/5.3/getting-started/introduction/