Python データベース(Flask-SQLAlchemy)

SQL資料 MySQL dataset

データベースの準備

Flask-SQLAlchemyは以下のようにしてインストールします。

pip install Flask-SQLAlchemy

VisualStudio CodeにSQLiteのファイルを扱う拡張機能「SQLite3 Editor」をインストールしておきます。コマンドラインで以下から可能です。

code --install-extension yy0931.vscode-sqlite3-editor

もしくはVisualStudio Code画面左の拡張機能のボタンを押して、「SQLite3 Editor」と入力します。

Flask-SQLAlchemyを使うには通常のようにFlaskのオブジェクトを作り、その設定でファイル名を指定します。また、SQLAlchemyのオブジェクトを作成します。

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hanbai.db'
db = SQLAlchemy(app)

モデルクラス

今回は商品を管理する以下のようなテーブルを作成します。

shouhinテーブル

そのために、商品テーブルのデータを一件入れるためのクラス、Shouhinを作成します。これはSQLAlchemyの「モデル」の作り方に従い作成します。「モデル」とはテーブルの一件のデータを入れる入れ物です。列名を以下のように指定します。

class Shouhin(db.Model):
    sid = db.Column(db.Integer, primary_key=True)
    sname = db.Column(db.String(255))
    tanka = db.Column(db.Integer)

    def __init__(self, sname, tanka):
        self.tanka = tanka
        self.sname = sname

    def __str__(self):
        return f"{self.sid} {self.sname} {self.tanka}"

db.Integerで整数型、db.Stringで文字列型になります。Stringの場合、最大文字数を指定します。また、sidは主キーなので primary_key=True も付加します。 さらにクラス内にコンストラクタを作成しています。インスタンスを生成する際に指定する項目を決めるためです。ここではsnameとtankaを指定します。 __str__ という関数は文字列化するときに自動で呼び出される関数です。printで表示をしやすくしています。

テーブル作成

テーブル作成

テーブルを作成するようには以下のように行います(いったん全削除して再作成しています)。with app.app_context() はweb環境では無いコンソールなどで必要な処理です。その配下にデータベース操作を記述します。

with app.app_context():
    db.drop_all() # テーブル全削除
    db.create_all() # テーブル作成

ここで実行すると、instanceフォルダにhanbai.dbというファイルが作成されます。これを開くとshouhinテーブルがあるはずです。

データ初期化

次に、初期データを設定します。最初にデータを配列に入れておきます。データはShouhinクラスのインスタンスとして生成します。

    # 初期データ追加
    data = [
        Shouhin(sname="りんご", tanka=100),
        Shouhin(sname="みかん", tanka=150),
        Shouhin(sname="いちご", tanka=120),
    ]
    db.session.add_all(data)
    db.session.commit()

データの操作

全検索

では、設定した全データを表示してみます。

Shouhin.query.all() でshouhinテーブルの全ての行を取得します。行は row と言いますので、rowsという変数に格納しています。それをfor文で r に取り出して表示しています。

    rows = Shouhin.query.all()
    for r in rows:
        print(r)

検索

Shouhin.query.filter(条件)で条件を指定しての検索が可能です。条件式では列名を クラス名.列名 のように書きます。

# tankaが120以上
rows = Shouhin.query.filter(Shouhin.tanka >= 120).all()

# 複数条件
rows = Uriage.query.filter(Uriage.sid == 1, Uriage.kosu > 1).all()

# 一件のみ
rows = Shouhin.query.filter(Shouhin.sid == 1).first()

# 文字列の検索
rows = Shouhin.query.filter(Shouhin.sname.contains("ご")).all()

主キーで検索する場合、get(主キー) または get_or_404(主キー) というメソッドも使うことが出来ます。主キーでの検索は常に一件しか返ってきませんので、rowsではなく、rowに入れます。

row = Shouhin.query.get(2)

row = Shouhin.query.get_or_404(2)

整列

全検索した結果を単価の昇順で並び替えます。これをqueryの後にorder_by関数を付けます。その引数に、db.asc("列名")、またはdb.desc("列名")を指定します。ascで昇順、descで降順になります。なお、昇順の場合には、db.asc()は省略して、"列名"だけでも可能です。

# 単価の昇順
rows = Shouhin.query.order_by('tanka').all()

# 単価の昇順
rows = Shouhin.query.order_by(db.asc('tanka')).all()

# 単価の降順
rows = Shouhin.query.order_by(db.desc('tanka')).all()

データ追加

データを一件追加するには、インスタンスを作成し、db.session.addで追加します。最後に db.session.commit() を行わないとこの変更が保存されません。

s = Shouhin(sname="ぶどう",tanka=300)
db.session.add(s)
db.session.commit()

データの変更

既存データを変更するには、まず、get_or_404(主キー)を使って指定した主キーのデータを取得します。そして、属性を書き換えます。最後にcommitします。

row = Shouhin.query.get_or_404(4)
row.sname = "なし"
row.tanka = 200
db.session.commit()

データの削除

既存データを削除するには、まず、get_or_404(主キー)を使って指定した主キーのデータを取得します。そして、db.session.delete(データ) とすれば削除されます。最後にcommitします。

row = Shouhin.query.get_or_404(4)
db.session.delete(row)
db.session.commit()

結合

結合を行う場合、まず一対多の「一」の方でモデルクラス内にて db.relationship を記述する。これは、多の方のクラスのリストを持つ、という意味である。このとき、backrefを設定することで「多」のクラスの方でも「一」の方をその名前で参照できる。

    uriage = db.relationship('Uriage', backref='shouhin', lazy=True)

「多」の方では、外部キーの設定を行う。

    sid = db.Column(db.Integer, db.ForeignKey('shouhin.sid'))

全体像

class Shouhin(db.Model):
    sid = db.Column(db.Integer, primary_key=True)
    sname = db.Column(db.String(255))
    tanka = db.Column(db.Integer)

    uriage = db.relationship('Uriage', backref='shouhin', lazy=True)

    def __init__(self, sname, tanka):
        self.sname = sname
        self.tanka = tanka

    def __str__(self):
        return f"{self.sid} {self.sname} {self.tanka}"
    
class Uriage(db.Model):

    uid = db.Column(db.Integer, primary_key=True)
    sid = db.Column(db.Integer, db.ForeignKey('shouhin.sid'))
    kosu = db.Column(db.Integer)
    hi = db.Column(db.Date)

    def __init__(self, sid , kosu):
        self.sid = sid
        self.kosu = kosu
        self.hi = datetime.today()

    def __str__(self):
        return f"{self.uid} {self.sid} {self.kosu} {self.hi}"

これにより多の方から一を参照できる。

    results = Uriage.query.all()
    for uriage in results:
        print(uriage.uid, uriage.shouhin.sname, uriage.kosu, uriage.hi)

また、一方から多を参照できる。

    rows = Shouhin.query.all()
    for r in rows:
        print(r)
        for u in r.uriage:
            print(u)