flask_SQLAlchemyを使ってCRUDを作成する

# はじめに
Flaskを用いて簡単なCRUDを再現することができたのでその方法を備忘録的に記事に残そうかと思います！
もし、誤りや補足などあればコメントしていただければありがたいです！

## 開発環境

python 3.7
anaconda

## 作成
#### フォルダの作成
以下のような構成で作業を進めていきます！
~~~
.
└── CRUD
    ├── templates
    │   ├── application.html
    │   ├── index.html
    │   ├── new.html
    │   └── edit.html
    └── app.py
~~~

### Flaskの導入
仮想環境を作成します。以下の２行をそれぞれ実行し、activateします。
```
conda create -n flask37 python=3.7

conda activate flask37
```

flaskをインストールします
```
conda install flask
```

### 共通部分の作成
全てのページに表示されるヘッダー部分を作成します。
```html:application.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>Document</title>
</head>
<body>
    <header>
        <h1>サンプルアプリ</h1>
    </header>
    {% block content %}
    {% endblock %}
</body>
</html>
```
一旦こんな感じで良いでしょう。
```
    {% block content %}
    {% endblock %}
```
の部分は各ページごとの内容が入ります。

### routingの作成と一覧ページの表示
```python:app.py
from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

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

```

```html:index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
{% endblock %}
```
application.htmlの内容を継承します。

### DBの作成
今回はflask＿SQLAlchemyを用いてDBを作成します。公式ドキュメントを参照して作りました

https://flask-sqlalchemy.palletsprojects.com/en/2.x/

まずはターミナルで以下を実行しましょう。
```:ターミナル
pip install -U Flask-SQLAlchemy

conda install pytz
```

またapp.pyを以下のように編集します。今回はPostというClassを作成し、title,bodyというカラムを作成します。

```python:app.py
from flask import Flask,render_template
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime


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

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(20), nullable=False)
    body = db.Column(db.String(140), nullable=False)


@app.route('/')
def index():
    return render_template('index.html')

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

```
次にインタプリタモードにして以下のコマンドを１行ずつ実行してDBを作成します。warningが出ますが無視して進めて大丈夫でした。
```:ターミナル
>>> from app import db

>>> db.create_all()
```
これでtest.dbというファイルが作成されていると思うので確認してみてください。

もしここでエラーが発生する場合は以下の記事の方法を試してみてください！
https://qiita.com/Yu_unI1/items/00110442d66a5f11f7b1

### 新規投稿
新しく投稿ができるようにしていきます。
まずはHTMLから
```html:new.html
{% extends "application.html" %}
{% block content %}
    <h1>新規登録</h1>
    <form method="POST">
        <label for="title">題名</label>
        <input type="text" name="title">
        <label for="body">本文</label>
        <input type="text" name="body">
        <input type="submit" value="新規登録">
    </form>
{% endblock %}
```
ここではinputタグのnameの値が重要になってきます。
次にroutingの設定をしていきましょう！
```python:app.py
@app.route('/new',methods=['GET','POST'])
def create():
    if request.method == 'POST':
        # POSTメソッドの時の処理。
        title = request.form.get('title')
        body = request.form.get('body')

        post = Post(title=title,body=body)
        # DBに値を送り保存する
        db.session.add(post)
        db.session.commit()
        return redirect('/')
    else:
        # GETメソッドの時の処理
        return render_template('new.html')
```
またここでimport文を以下のように変更します！
request,redirectを追加しました。
```python:app.py
from flask import Flask,render_template,request,redirect
```
index.htmlに新規投稿ページへのリンクを貼ります。

```html:index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
    <!-- 下記１行を追加 -->
    <a href="/new">新規作成画面</a>
{% endblock %}
```
ここで１度動作確認をしましょう。入力フォームに値を入力し、送信するとindexページに戻ることを確認しておいてください。

### 投稿の表示
次はDBに送られた投稿を表示します。
まずはapp.pyから編集します。
```python：app.py
@app.route('/')
def index():
    posts = Post.query.all()
    return render_template('index.html',posts = posts)
```
変数postsにPostの中の全投稿を代入します。returnのところではHTML側からもpostsの中身を扱うことができるように、posts = postsで値を引き渡しています。

次に、html側で表示ができるようにします。具体的にはpostsの中身をfor文で回して一つずつ投稿内容を表示します。
index.htmlを以下のように編集しましょう。
```html:index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
    <a href="/new">新規作成画面</a>
    {% for post in posts %}
    <div>
        <p>{{post.id}} : <b>{{post.title}}</b> {{post.body}}</p>
    </div>
    {% endfor %}
{% endblock %}
```
### 投稿の編集
投稿を編集できるようにします。
まずはrouthingの設定をします。
```python:app.py
@app.route('/<int:id>/edit',methods=['GET','POST'])
def update(id):
    post = Post.query.get(id)
    if request.method == 'GET':
        return render_template('edit.html',post=post)
    else:
        post.title = request.form.get('title')
        post.body = request.form.get('body')
        db.session.commit()
        return redirect('/')
```
編集であるためどの投稿かを識別する必要があります。なのでroutingをidごとに変えています。その値を<int:id>で受け取ります。getメソッドでアクセスされた場合は編集画面を表示、Postメソッドでアクセスされた場合は編集を行います。新規投稿とほんとんど同じであることがわかると思います。

次にHTML側を編集しましょう。
```html:edit.html
{% extends "application.html" %}
{% block content %}
    <h1>編集</h1>
    <form method="POST">
        <label for="title">タイトル</label>
        <input type="text" name="title" value = {{post.title}}>
        <label for="body">内容</label>
        <input type="text" name="body" value = {{post.body}}>
        <input type="submit" value="編集">
    </form>
{% endblock %}
```
new.htmlとほとんど同じですね。
最後にindex.htmlにeditへの遷移を実装する１行を追記しましょう。
```html:index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
    <a href="/new">新規作成画面</a>
    {% for post in posts %}
    <div>
        <p>{{post.id}} : <b>{{post.title}}</b> {{post.body}}</p>
        <!-- 下の１行を追加 -->
        <a href="{{post.id}}/edit">編集</a>
    </div>
    {% endfor %}
{% endblock %}

```

### 投稿の削除
最後に削除ができるようにしましょう。
まずはroutingを書きましょう。これもどの投稿を削除するかによってroutingが変わるのでidを含めたroutingを行う必要がありますね。
```python:app.py
@app.route('/<int:id>/delete',methods=['GET'])
def delete(id):
    post = Post.query.get(id)
    #投稿を削除
    db.session.delete(post)
    #削除を反映
    db.session.commit()
    return redirect('/')
```
削除に新しくHTMLを作成する必要はないので、indexに削除ボタンを実装して終了です。
```html:index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
    <a href="/new">新規作成画面</a>
    {% for post in posts %}
    <div>
        <p>{{post.id}} : <b>{{post.title}}</b> {{post.body}}</p>
        <a href="{{post.id}}/edit">編集</a>
        <!-- 下の１行を追加 -->
        <a href="{{post.id}}/delete">削除</a>
    </div>
    {% endfor %}
{% endblock %}
```
これで一通りの実装ができていると思うので、確認してみてください！

## 最後に
備忘録として書いた記事でしたがいかがでしたでしょうか。もしこの記事が誰かの助けになったなら幸いです。

読んでいただきありがとうござました！！

## 参考記事

https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/

https://flask-sqlalchemy.palletsprojects.com/en/2.x/