Contents
Flaskの勉強がてら、Flask公式のチュートリアルを
Flask-SQLAlchemyとFlask-Migrate を使って作ってみました。
リポジトリはこちら
(Flask-DebugToolbar も入れています)
スポンサードサーチ
Flask-SQLAlchemy
公式チュートリアルだと生SQLだったので、ラッパーを使いたくて導入。
設定方法
QuickStart に書いてあるけれど、SQLALCHEMY_DATABASE_URI
を設定すればよさげ。
今回はconfig.pyに設定を書くことにしました。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@{host}/{db_name}?charset=utf8'.format(**{
'user': "root",
'password': "root",
'host': "db_flaskr",
'db_name': "flaskr"
})
DB設定の内容はdocker-compose.ymlに記載しています。
db_flaskr:
image: mysql:latest
restart: always
environment:
MYSQL_PORT: 3306
MYSQL_ALLOW_EMPTY_PASSWORD: 1
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: flaskr
MYSQL_USER: root
MYSQL_PASSWORD:
TZ: ${TZ:-Asia/Tokyo}
ちなみに flaskr
っという名称は公式から持ってきてます。
このチュートリアルはFlaskrと呼ぶ基本的なブログのアプリケーションの作成を一通り行います。
モデルを設置
Simple Relationships を参考にしただけです。
公式のテーブル設計 はイケてないので若干内容を変更しています。
例1:
テーブル名を複数形に。
userテーブル → usersテーブル
例2:
userテーブルにusernameカラムがあるが、カラム名にもuserを付けるのは冗長なので。
users.username → users.name
from application import db
from datetime import datetime
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
from application import db
from datetime import datetime
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
title = db.Column(db.Text, nullable=False)
body = db.Column(db.Text, nullable=False)
users = db.relationship('User', backref=db.backref('posts', lazy=True))
SQL操作
基本的なSQL操作の書き方
Select, Insert, Delete を参照。
(公式なのにUpdateの書き方は載ってない?)
Select
例1:
usersテーブルのレコード取得
SELECT
*
FROM
users
from app.models import User
User.query.all()
例2:
postsテーブルとusersテーブルをJOINしposts.created_at でソートした結果
(modelにリレーションの記載が必要)
SELECT
*
FROM
posts
JOIN
users ON users.id = posts.user_id
ORDER BY
posts.created_at
from app.models import Post
Post.query.join(Post.users).order_by(Post.created_at).all()
Insert
db.session.add()
後にdb.session.commit()
を実行すれば良さそうです。
例:
投稿内容(タイトルと本文) の新規登録
(user_idはsessionから取得した users.id = 1 とします)
INSERT INTO
posts
(user_id, title, body)
VALUES
(1, 'xxxxxx', 'xxxxxx')
from app.models import Post
from flask import Flask, request, session
title = request.form['title']
body = request.form['body']
new_post = Post(title=title, body=body, user_id=session.get('user_id'))
db.session.add(new_post)
db.session.commit()
Update
get_or_404 があるので、主キーによりレコードの存在チェックを行い、db.session.commit()
を実行すれば良さそうです。
例:
投稿内容(タイトルと本文) の更新
(posts.idは1とします)
UPDATE
posts
SET
title = 'xxxxxx'
AND body = 'xxxxxx'
AND updated_at = now()
WHERE
id = 1
from app.models import Post
from datetime import datetime
from flask import Flask, request, session
post = Post.query.get_or_404(1)
title = request.form['title']
body = request.form['body']
post.title = title
post.body = body
post.updated_at = datetime.now()
db.session.commit()
Delete
get_or_404 で、主キーによりレコードの存在チェックを行い、db.session.delete()
後にdb.session.commit()
を実行すれば良さそうです。
別にドキュメントでいうme
が担保されれば、get_or_404
でなくても良いと思う。
( me
ってなんだ。。。)
Deleting records is very similar, instead of
add()
usedelete()
:>>> db.session.delete(me)
>>> db.session.commit()
例:
投稿内容(タイトルと本文) の削除
(posts.idは1とします)
DELETE
FROM
posts
WHERE
id = 1
from app.models import Post
from flask import Flask, session
post = Post.query.get_or_404(1)
db.session.delete(post)
db.session.commit()
Flask-Migrate
Migration管理がしたくて導入
(Flaskでもできる?)
設定方法
サンプル通り。
サンプルを見た感じだとFlask-SQLAlchemy
をインストールしていないと使えないっぽい。Flask-SQLAlchemy
を設定したときのに下記2行を追加することで、flask db
コマンドが実行できるようになる。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
+ from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
+ migrate = Migrate(app, db)
初期化
$ flask db init
migrationsディレクトリが作成される。
※ 今回のリポジトリからcloneしてきた場合は、すでに作成されているので実行する必要はありません。
revisionファイル作成
$ flask db migrate
モデルとDBの差分を見て生成されるため、
テーブル設計を変更する場合、基本は
モデルファイルを編集 → flask db migrate
実行の順になる。
実行すると、以下のようなrevisionファイルが作成される。
※ 初回の場合はdowngrade先がないのでdown_revision = None
となる。
"""empty message
Revision ID: 47224eeeced1
Revises:
Create Date: 2020-xx-xx xx:xx:xx.xxxxxx
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '47224eeeced1'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
op.create_table('users',
xxxxxx
),
op.create_table('posts',
xxxxxx
)
def downgrade():
op.drop_table('posts')
op.drop_table('users')
もう一度実行するとdowngrade先が入る。
"""empty message
Revision ID: 050dddb4596b
Revises: 47224eeeced1
Create Date: 2020-xx-xx xx:xx:xx.xxxxxx
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '050dddb4596b'
down_revision = '47224eeeced1'
branch_labels = None
depends_on = None
def upgrade():
xxxxxx
def downgrade():
xxxxxx
migration実行
$ flask db upgrade
最新バージョンの状態になるように実行されます。
ダウングレード
$ flask db downgrade
現在のバージョンから1つ前のバージョンに戻ります。
どれが1つ前のバージョンかはrevisionファイルのdown_revision
で管理している。
当然、revisionファイルにdef downgrade():
の記載がないと動作しない。
バージョン確認
$ flask db current
現在のバージョンを確認できる。
どれが現在のバージョンかはDBのalembic_version
テーブルで管理しているっぽい。
mysql> select * from alembic_version;
+--------------+
| version_num |
+--------------+
| 6b15f8547990 |
+--------------+
1 row in set (0.00 sec)
参考
Flask-SQLAlchemyに関しては、
(英語だけど) この動画がすごい参考になった。