概要
Flask + SQLAlchemyを利用してテーブル追加・削除、レコードの登録・更新・削除、検索、リレーションを利用した検索を実行する。
データベースはSQLite使用。
バージョン情報
- Flask==1.1.1
- Flask-SQLAlchemy==2.4.1
ファイル構成
サンプルコードは下記のような構成で作成します。
├── app.py ├── example.db └── models ├── child.py └── example.py
example.dbはSQLiteのファイルです。
modelsに入っているexample.pyが親テーブルで、child.pyはそれに紐づく子テーブルを実装します。
dbの指定
下記のような記述で app.py というファイルを作成します。
import flask import flask_sqlalchemy app = flask.Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_ECHO'] = True db = flask_sqlalchemy.SQLAlchemy(app) if __name__ == '__main__': app.run()
これを実行すると example.db というファイル名でSQLIteのDBファイルが生成されます。
SQLALCHEMY_ECHOをTrueに設定すると実行されるSQLが標準出力されます。
モデルの生成
models/example.py に sqlalchemy のモデルファイルを作成します。
import datetime from app import db class Example(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) age = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.datetime.now) updated_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
app.pyで記述したdbを参照しています。
id, name age, created_at, updated_atの5つのカラムを持つテーブルを定義しています。
テーブルの生成
テーブルの生成は指定のモデルをimportした後、db.create_all()することで実行できます。
from models.example import Example from app import db from models.child import Child db.create_all()
実行されるSQL
CREATE TABLE example ( id INTEGER NOT NULL, name VARCHAR NOT NULL, age INTEGER, created_at DATETIME, updated_at DATETIME, PRIMARY KEY (id) )
sqlite3のコマンドから生成されたDBを確認してみます。
$ sqlite3 example.db > .tables example > .schema CREATE TABLE example ( id INTEGER NOT NULL, name VARCHAR NOT NULL, age INTEGER, created_at DATETIME, updated_at DATETIME, PRIMARY KEY (id) );
INSERT
db.session.addでデータを追加します。
from models.example import Example from app import db example = Example() example.id = 1 example.name = 'foo' example.age = 100 db.session.add(example) db.session.commit()
実行されるSQL
BEGIN (implicit) INSERT INTO example (id, name, age, created_at, updated_at) VALUES (?, ?, ?, ?, ?) -- (1, 'foo', 100, '2020-02-03 18:07:34.819812', '2020-02-03 18:07:34.819825') COMMIT
DELETE
モデルを指定してDELETEする場合の記述。
db.session.delete(example) db.session.commit()
実行されるSQL
DELETE FROM example WHERE example.id = ? -- (1,) COMMIT
条件に合致するレコードをDELETEする場合。
Example.query.filter_by(name = 'foo').delete()
db.session.commit()
実行されるSQL
BEGIN (implicit) DELETE FROM example WHERE example.name = ? -- ('foo',) COMMIT
UPDATE
updateはモデルの内容を書き換えてコミットすると反映されます。
# 更新対象の行を取ってくる row = Example.query.get(1) # update row.name = 'bar' db.session.commit()
実行されるSQL
BEGIN (implicit) SELECT example.id AS example_id, example.name AS example_name, example.age AS example_age, example.created_at AS example_created_at, example.updated_at AS example_updated_at FROM example WHERE example.id = ? -- (1,) UPDATE example SET name=?, updated_at=? WHERE example.id = ? -- ('bar', '2020-02-03 18:09:53.557883', 1) COMMIT
1レコードの更新ではなく、条件に合致した複数行を更新する場合。
Example.query.filter(Example.age > 10).update({'name': 'new_name'}) db.session.commit()
実行されるSQL(2レコードを投入して実行)
BEGIN (implicit) UPDATE example SET name=?, updated_at=? WHERE example.age > ? -- ('new_name', '2020-02-03 18:21:07.597725', 10) COMMIT
検索
IDで検索
row = Example.query.get(1)
実行されるSQL
BEGIN (implicit) ELECT example.id AS example_id, example.name AS example_name, example.age AS example_age, example.created_at AS example_created_at, example.updated_at AS example_updated_at FROM example WHERE example.id = ? -- (1,)
条件検索して first() で1行目を取得
row = Example.query.filter_by(name='bar').first()
実行されるSQL
SELECT example.id AS example_id, example.name AS example_name, example.age AS example_age, example.created_at AS example_created_at, example.updated_at AS example_updated_at FROM example WHERE example.name = ? LIMIT ? OFFSET ? -- ('bar', 1, 0)
条件に合致するレコードをループ
for row in Example.query.filter(Example.age > 10): print(row)
relationの設定
example.pyとchild.pyの間に1対多のリレーションを貼ってみます。
child.pyの記述
import datetime from app import db class Child(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) example_id = db.Column(db.Integer, db.ForeignKey('example.id'), nullable=False) created_at = db.Column(db.DateTime, default=datetime.datetime.now) updated_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
childのexample_idカラムに exampleテーブルのidカラムに対してForeignKeyが設定されています。
example.py側にchildへの参照を記述します。
import datetime from app import db class Example(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) age = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.datetime.now) updated_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now) childs = db.relationship('Child')
これで Exampleクラスのchildsにアクセスすればchildの値が取れます。
1度テーブルを再生成してすっきりさせます。
from app import db from models.example import Example from models.child import Child db.drop_all() db.create_all()
テストデータを入れます。
example = Example() example.id = 1 example.name = 'foo' example.age = 100 db.session.add(example) child = Child() child.id = 1 child.name = 'chid1' child.example_id = 1 db.session.add(child) child = Child() child.id = 2 child.name = 'chid2' child.example_id = 1 db.session.add(child) db.session.add(example) db.session.commit()
Exampleからchildsを取得してみます。
childs = Example.query.get(1).childs for child in childs: print(child) #=> <Child 1> #=> <Child 2>
ちゃんとexample.idに紐づくレコード2件が取れました。
実行されるSQL
SELECT example.id AS example_id, example.name AS example_name, example.age AS example_age, example.created_at AS example_created_at, example.updated_at AS example_updated_at FROM example WHERE example.id = ? -- (1,) SELECT child.id AS child_id, child.name AS child_name, child.example_id AS child_example_id, child.created_at AS child_created_at, child.updated_at AS child_updated_at FROM child WHERE ? = child.example_id -- (1,)
app.pyへの参照を避ける
本例ではコードをシンプルにする為にapp.pyのdb変数に対してアクセスしていますが、実際に開発する際はそれは避けて下記のようなパターンを用います。
https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/
改定履歴
Author: Masato Watanabe, Date: 2020-02-03, 記事投稿