🎃

flask_SQLAlchemyを使ってCRUDを作成する

2022/11/03に公開

はじめに

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

こちらの記事は Qiitaに以前投稿した以下の記事と同一の内容となっています。練習がてらzennに投稿してみたものなので、その点ご了承ください🙇
https://qiita.com/Yu_unI1/items/316e03d94f276695ff13

開発環境

python 3.7
anaconda

作成

フォルダの作成

以下のような構成で作業を進めていきます!

.
└── CRUD
    ├── templates
    │   ├── application.html
    │   ├── index.html
    │   ├── new.html
    │   └── edit.html
    └── app.py

Flaskの導入

仮想環境を作成します。以下の2行をそれぞれ実行し、activateします。

conda create -n flask37 python=3.7

conda activate flask37

flaskをインストールします

conda install flask

共通部分の作成

全てのページに表示されるヘッダー部分を作成します。

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の作成と一覧ページの表示

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)

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というカラムを作成します。

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)

次にインタプリタモードにして以下のコマンドを1行ずつ実行してDBを作成します。warningが出ますが無視して進めて大丈夫でした。

ターミナル
>>> from app import db

>>> db.create_all()

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

新規投稿

新しく投稿ができるようにしていきます。
まずは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の設定をしていきましょう!

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を追加しました。

app.py
from flask import Flask,render_template,request,redirect

index.htmlに新規投稿ページへのリンクを貼ります。

index.html
{% extends "application.html" %}
{% block content %}
    <h1>投稿一覧</h1>
    <!-- 下記1行を追加 -->
    <a href="/new">新規作成画面</a>
{% endblock %}

ここで1度動作確認をしましょう。入力フォームに値を入力し、送信するとindexページに戻ることを確認しておいてください。

投稿の表示

次はDBに送られた投稿を表示します。
まずは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を以下のように編集しましょう。

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の設定をします。

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側を編集しましょう。

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への遷移を実装する1行を追記しましょう。

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>
        <!-- 下の1行を追加 -->
        <a href="{{post.id}}/edit">編集</a>
    </div>
    {% endfor %}
{% endblock %}

投稿の削除

最後に削除ができるようにしましょう。
まずはroutingを書きましょう。これもどの投稿を削除するかによってroutingが変わるのでidを含めたroutingを行う必要がありますね。

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に削除ボタンを実装して終了です。

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>
        <!-- 下の1行を追加 -->
        <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/

Discussion