【初めてのWebアプリ開発】 Flaskを用いたTODOアプリ

所属するコミュニティで初心者向けWebアプリハンズオンとして、Flaskを用いてTODOアプリを作りました。
その時の資料を一部修正して共有します。
これからWebアプリ開発を学ぶ方や、ハンズオン担当者の方の参考になれば幸いです。


:::note
対象
Python, HTML, CSSの基本を学んだ初心者
:::
:::note
目標
簡易的なWebアプリを作成する
:::



# Webアプリケーションについて
Webアプリは大きく2つの分野に分けられます。
__フロントエンド・・・ユーザーが見える部分・直接操作する部分__
__バックエンド　・・・ユーザーから見えない部分・直接操作できない部分__

電卓アプリを例に挙げると、
電卓アプリのデザインを作ったり、数字を入力するのがフロントエンドで、入力された値を処理するのがバックエンドといった形になります。
また、計算結果を保存したい場合やユーザー情報を保存したい場合はデータベースを使用します。
## データベースとは
データを効率的かつ整理された方法で保存・管理するシステムのこと

アプリ開発では大量のデータが必要になります。
これをリストや配列、テキストファイルなどで管理することは極めて扱いにくく非効率的です。
DBを使用することで、複数のデータを関連づけて保存することができたり、検索機能を使えたりなど効率的にデータを扱うことができます。
（DataBaseの頭文字をとってDBと呼ばれることが多い）


## Flaskとは
Flask（フラスク）とは、Pythonのフレームワークです。
今回はこのフレームワークを使って簡単なTODOアプリをつくります！

:::note
**フレームワークとは**
Webアプリやシステムを開発するために必要な機能があらかじめ用意された枠組み。

Webアプリを0から開発することはとても大変ですが、
あらかじめ必要な機能が用意されたフレームワークを使用することで、効率的にアプリ開発を行うことができます。
:::

# TODOアプリを作ろう
## 完成系
![画面収録 2024-01-02 0.44.14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984587/52ecb2b7-9ea8-a774-7f34-bfa04cd99b37.gif)


### GitHubリポジトリ
https://github.com/NonokaM/flask-todo-example


## 環境構築
環境構築とは、
自分のPC上で言語やライブラリが使用できる環境を用意してあげることです。

今回使用する技術
・　Python
・　Flask
・　HTML, CSS
・　SQLite
Pythonの環境は準備済みです。
HTML, CSSはWebブラウザ（ChromeやEdge）に標準で搭載されています。

### Flaskのインストール
ターミナルに下記のコマンドを入力します。
```
pip install Flask
```
Pythonは公式HPからファイルをダウンロードしたり設定を行う必要がありましたが、
FlaskはあくまでもPythonを利用したフレームワークなのでこれだけでOKです。

### SQLAlchemyのインストール
ターミナルに下記のコマンドを入力します。
```
pip install flask-sqlalchemy
```
Flaskにおいてデータベースを簡単に操作できるライブラリです。
今回はこれだけで環境構築完了です！


## Flask で Hello World
まず、FlaskでHello Worldを表示させてみましょう。
```app.py```というファイルを作成して、下記のコードを追加します。
```
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World'

if __name__ == "__main__":
    app.run(debug=True)
```
ファイルを指定して実行しましょう。
```
python app.py
```


成功すると、以下のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984587/29c44d95-4faa-9fe3-eaa9-b0b9e031b217.png)
```* Running on http://127.0.0.1:5000```をCTRL+クリックでページにとび確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984587/e6ab0d14-14f0-434a-03e2-e7b3edf89c7a.png)
Hello Worldと表示されていれば成功です！
たったこれだけのコードで、簡易的ではありますがWebアプリが完成しました！Flaskすごい！


<details><summary>フレームワークを使用しない場合</summary>

下記のコードは、フレームワークを使用せずPythonのみで実装した場合です。
コードの記述量が多い上に、サーバー周りの処理も書く必要があるため複雑になります。
また安全性も低いためフレームワークの使用が推奨されます。
```
import socket

def handle_client(client_socket):
    request = client_socket.recv(1024)
    print(f"Received: {request}")
    client_socket.send(b"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello World")
    client_socket.close()

def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("0.0.0.0", 8000))
    server.listen(5)
    print("Listening on port 8000")

    while True:
        client, addr = server.accept()
        print(f"Accepted connection from: {addr[0]}:{addr[1]}")
        handle_client(client)

if __name__ == "__main__":
    main()
```
</details>


## 必要なファイルの準備
以下のフォルダとファイルを作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984587/202a54de-ca6e-dd03-dc5b-eef92f04e12b.png)

今回はFlaskについて詳しく解説していくので、HTML, cssは事前に用意したものをそのまま使ってください。
index.htmlファイルに以下のコードを追加
```
<!DOCTYPE html>
<html>
<head>
    <title>Todo App</title>
    <link rel="stylesheet" href="../static/style.css">
</head>
<body>
    <div class="container">
        <h1>Todo App</h1>
        <form method="POST" action="/add">
            <input type="text" name="title" id="title" placeholder="Add Todo" required>
            <button type="submit">Add</button>
        </form>
        <ul>
            {% for todo in todo_list %}
            <li>
                {{ todo.title }}
                <form method="POST" action="/delete/{{ todo.id }}">
                    <button type="submit">Delete</button>
                </form>
            </li>
            {% endfor %}
        </ul>
    </div>
</body>
</html>
```



## データベースを準備する
データベースの内容は今回範囲外とします。ここはコピペしましょう！
app.pyに以下の内容を貼り付けてください。
```
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
db = SQLAlchemy(app)

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))


### タスクを表示する ###

### タスク追加 ###

### タスク削除 ###


if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)

```



## ルーティングの解説
Flaskではルーティングという仕組みを使って、URLに応じた処理を定義することができます。

ルーティングとは、特定のURLへのアクセスがあった際に、どの関数を実行するかを決定するルールのことです。
これにより、WebアプリケーションはユーザーがアクセスしたURLに対応する適切なレスポンスを提供することができます。

Flaskでのルーティングは、デコレータというPythonの機能を利用して行います。具体的なコードを見てみましょう：




具体的なコードの例を見てみましょう。

```
from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return 'This is the home page.'

@app.route('/about')
def about():
    return 'This is the about page.'

```
この例では、@app.route('/')というデコレータはホームページへのアクセスがあったときにhome関数を実行することを指定しています。

同様に、@app.route('/about')というデコレータは/aboutというURLへのアクセスがあったときにabout関数を実行することを指定しています。

それぞれの関数内部では、return文でユーザーに表示する文字列を指定しています。
今回は、HTMLのファイルを返すことでHTMlファイルと情報を表示させていきます。





### タスクを表示する ###
```
@app.route("/", methods=["GET", "POST"])
def home():
    # データベースから全てのTodoレコードを取得
    todo_list = Todo.query.all()
    # 取得したTodoリストを"index.html"テンプレートに渡し、ウェブページとして表示
    return render_template("index.html", todo_list=todo_list)
```


### タスク追加 ###
```
@app.route("/add", methods=["POST"])
def add():
    # ユーザーから送信されたフォームデータからタイトルを取得
    title = request.form.get("title")
    # 新しいTodoオブジェクトを作成
    new_todo = Todo(title=title)
    # 新しいTodoをデータベースセッションに追加
    db.session.add(new_todo)
    # 変更をデータベースにコミット
    db.session.commit()
    # タスク追加後、ホームページにリダイレクト
    return redirect(url_for("home"))
```

### タスク削除 ###
```
@app.route("/delete/<int:todo_id>", methods=["POST"])
def delete(todo_id):
    # URLから渡されたIDに基づいて、該当するTodoをデータベースから取得
    todo = Todo.query.filter_by(id=todo_id).first()
    # 取得したTodoをデータベースセッションから削除
    db.session.delete(todo)
    # 変更をデータベースにコミット
    db.session.commit()
    # タスク削除後、ホームページにリダイレクト
    return redirect(url_for("home"))
```


## これでTODOアプリの機能が作成できました。
最後に見た目を整えるためにcssを追加しましょう。
cssの解説がとてもとても長くなりそうなので用意したcssを追加しましょう。
style.cssに以下のコードを追加
```
body {
    background-color: #fff;
    font-family: Arial, sans-serif;
    color: #333;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}


.container {
    text-align: center;
    width: 500px;
    box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
    padding: 30px;
    border-radius: 15px;
}


h1 {
    color: #333;
    margin-bottom: 30px;
}


input[type="text"] {
    width: 70%;
    padding: 10px;
    border: none;
    border-bottom: 2px solid #333;
    outline: none;
    transition: border-bottom 0.3s;
}


input[type="text"]:focus {
    border-bottom: 2px solid #ff5252;
}


button {
    padding: 10px 20px;
    border: none;
    background-color: #ff5252;
    border-radius: 3px;
    align-items: center;
    color: #fff;
    cursor: pointer;
    transition: background-color 0.3s;
}


button:hover {
    background-color: #e04040;
}


ul {
    list-style: none;
    padding-left: 0;
    margin: 40px 0 15px;
}


li {
    background-color: #f5f5f5;
    padding: 18px 20px;
    margin-bottom: 10px;
    border-radius: 3px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}


li form {
    display: inline;
}


```