flaskで作る癖のない体重管理アプリ

# FlaskにはCRUD処理までの入門用チュートリアルがない。
## 癖のないFlaskのチュートリアルが欲しかった
Flaskってシンプルなアプリだったらファイル構成が単純でとても使いやすいですよね。
ただ覚えたてのころに色々なサイトを見てみるとやり方が全然違うので戸惑います。
公式のやり方さえ全然違う。DjangoでいうDjango Girl的なサイトが無いんですよ。
初歩的な紹介か、知ってる人向けの両極端な情報を拾ってしまう状態でした。
Djangoだと逆に仰々しくデカすぎる。
社内で本格的なGUI作るのが面倒だから代替のものとして利用しようという怠惰な動機で作ってる程度なので、ちょうどいい情報を探すの大変でした。

# とりあえず癖のないやつを作っておこう
無いのなら作ってしまえ、これぞFlaskっぽいやつ！っていう参考になるものを置いておこうと思います。自分の備忘録にも。チュートリアル的にご利用ください。CRUD処理がついたBootstrap5で飾りがついたズボラなものを作っていきます。
最終的には下のような画面ができます。
「Flaskって便利だな、楽しいな」と思ってくれると幸いです。

![Screen Shot 2021-12-20 at 23.08.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655653/b7144650-2d2e-f84d-824c-9a2d82b299c9.png)

## 必要なライブラリ

```
pip install flask
pip install flask-sqlalchemy
```

## フォルダ構成
app.pyとtemplatesフォルダというシンプルな構成。
templatesフォルダに各CRUD処理用のhtmlが入っています。

templates
     ├base.html
     ├index.html
     ├new.html
     ├delete.html
     └update.html

app.py

## app.pyの中身

``` app.py
from flask import Flask,render_template,url_for,request,redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)

# DB関係
#---------------------------------------------------------------------
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
db =SQLAlchemy(app)

class Weight(db.Model):
    __tablename__ ="weight"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    register_at = db.Column(db.Date)
    weight = db.Column(db.Float)

db.create_all()
#---------------------------------------------------------------------


# ルーティング関係
# ---------------------------------------------------

# ホーム　体重表一覧
@app.route("/")
def index():
    items = Weight.query.all()
    return render_template("index.html",items= items)

# データ新規作成
@app.route("/new",methods=["GET","POST"])
def new():
    if request.method =="POST":
        item = Weight()
        item.register_at = datetime.strptime(request.form['register_at'], "%Y-%m-%d")
        item.weight = request.form['weight']
        db.session.add(item)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template('new.html')


# データ更新
@app.route("/update/<int:id>", methods=["GET", "POST"])
def update(id):
    item = Weight.query.get(id)
    if request.method == "POST":
        item.register_at = datetime.strptime(request.form['register_at'],"%Y-%m-%d")
        item.weight = float(request.form['weight'])
        db.session.add(item)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template('update.html',item=item)


# データ削除
@app.route("/delete/<int:id>", methods=["GET","POST"])
def delete(id):
    item = Weight.query.get(id)
    if request.method == "POST":
        db.session.delete(item)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template("delete.html",item=item)


if __name__ == '__main__':
    app.run(debug=True)
```

## base.html
base.htmlのlink先はbootstrap5のCDNです。そのままコピーしたら機能します。

```base.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">
    <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>
        <title>体重推移</title>
</head>

<body>
    <div class="container">
    {% block content %}{% endblock %}
    </div>
</body>
</html>
```
## index.html
```index.html
{% extends 'base.html' %}
{% block content %}
    <h1>体重記録表</h1>
    <a class="btn btn-primary" href="/new" >新規追加</a>
        <table class="table table-hover">
            <thead>
                <th>登録日</th>
                <th>体重</th>
                <th></th>
                <th></th>
            </thead>
            <tbody>
                {% for item in items %}
                <tr>
                    <td>{{ item.register_at }}</td>
                    <td>{{ item.weight }}</td>
                    <td><a class="btn btn-success" href="/update/{{ item.id }}">更新</a></td>
                    <td><a class="btn btn-danger" href="/delete/{{ item.id }}">削除</a></td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
{% endblock %}
```
## new.html
```new.html
{% extends 'base.html' %}
{% block content %}
<h2>体重データ追加フォーム</h2>
<form method="POST" action="/new">
    <div class="form-group">
        <label for="register_at"></label>
        <input class="form-control" type="date" name="register_at">
    </div>
    <div class="form-group">
        <label for="weight"></label>
        <input class="form-control" type="text" name="weight"> 
    </div>
    <br>
    <input type="submit" class="btn btn-primary">
</form>
{% endblock %}
```

## delete.html
```delete.html
{% extends 'base.html' %}
{% block content %}
<h2>体重データ削除画面</h2>
<br>
<form method="POST" action="/delete/{{item.id}}">
    <div class="card">
        <div class="card-body">
            <p>こちらのデータを削除しますがよろしいですか？</p>
            <p>登録日:{{ item.register_at }}</p>
            <p>体重:{{ item.weight }}kg</p>
            <input type="submit" class="btn btn-danger" value="削除">
        </div>
    </div>
</form>
{% endblock %}

```

## update.html
```update.html
{% extends 'base.html' %}
{% block content %}
<h2>体重データ更新フォーム</h2>
<form method="POST" action="/update/{{item.id}}">
    <div class="form-group">
        <label for="register_at"></label>
        <input class="form-control" type="date" name="register_at" value="{{ item.register_at }}">
    </div>
    <div class="form-group">
        <label for="weight"></label>
        <input class="form-control" type="text" name="weight" value="{{ item.weight }}">
    </div>
    <br>
    <input type="submit" class="btn btn-primary">
</form>
{% endblock %}
```


## あとはターミナルで実行
```
python app.py
```

これで動くと思いますが、なにか問題がありましたらご連絡ください。