連載
» 2018年08月07日 05時00分 公開

Visual Studio Codeで始めるPythonプログラミング:VS CodeとFlask-SQLAlchemyでデータベース操作 (1/2)

Flask用のSQLAlchemy拡張機能「Flask-SQLAlchemy」の使い方を概観し、ToDoリストをデータベースに保存してみよう。

[かわさきしんじ,Insider.NET編集部]
「Visual Studio Codeで始めるPythonプログラミング」のインデックス

連載「Visual Studio Codeで始めるPythonプログラミング」

 前回は簡単なToDoリストアプリを作りながら、Flaskを利用したWebアプリ開発で使用するいろいろな事項(Flaskが提供する各種メソッドやオブジェクト、Jinja2テンプレート、静的ファイルの扱いなど)を見た。今回はFlask-SQLAlchemyを利用して、このToDoリストアプリにデータベースを組み込んでみよう。

 ただし、その前に「最小限のFlask-SQLAlchemyアプリ」を作り、それを使って、データベースの作成と操作を行う方法を見ていくことにする。

Flask-SQLAlchemyの基礎知識

 SQLAlchemyはPython用の著名なSQLツールキット/ORMの1つであり、これをFlaskで利用するための拡張機能としてFlask-SQLAlchemyがある。まずはFlask SQLAlchemyを使って、データベースを利用する方法を見てみよう。なお、ここではデータベースとしてはお手軽に試せる(Pythonには標準でsqlite3モジュールが含まれているので)SQLite3を例に取る。

 また、以下ではVS Code内で仮想環境「myenv」を作成し、そこにFlask(バージョン1.0.2)とFlask-SQLAlchemy(バージョン2.3.2)をインストールしている(「pip install flask-sqlclchemy」コマンドで両者をインストールできる)。環境としてはWindows版のVS Code 1.25.1で動作を検証している。

最小限のアプリ

 FlaskアプリからFlask-SQLAlchemyを利用するには、前回までに見た手順でFlaskアプリのオブジェクトを構築した上で、Flask-SQLAlchemyが提供するSQLAlchemyクラスにそのアプリオブジェクトを渡してやる。これを行うコードが以下になる(app.pyファイル)。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

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

Flaskアプリを作成して、それをSQLAlchemyクラスのコンストラクタに渡して、インスタンスを作成

 Flask-SQLAlchemyでは使用するデータベースをFlaskアプリの構成情報「SQLALCHEMY_DATABASE_URI」で指定する必要がある。また、その指定には上でも見たようにURIを使用する。ここでは、「sqlite:///db/sample.db」としているが、これはSQLiteを使用する例となる。これは「プロジェクトフォルダにあるdbサブフォルダの下にあるsample.dbファイル」をデータベースとして利用することを意味する。Flask-SQLAlchemyは他にも多くのデータベースをサポートしており、一般的には「dialect+driver://<ユーザー名>:<パスワード>@<ホスト>:<ポート>/<データベース名>」のようになる(例:mysql://someuser:somepasswd@localhost/mydatabase)。具体的には「Connection URI Format」を参照されたい。

 app.configオブジェクトは辞書形式で構成情報を保存/取得することが可能で、Flask-SQLAlchemyでは「SQLALCHEMY_DATABASE_URI」の他にも幾つかのキーがサポートされている。これらの中でよく目にすると思われるのは「SQLALCHEMY_TRACK_MODIFICATIONS」だ。これをTrueにすると、Flask-SQLAlchemyがデータベースの変更を追跡管理して、シグナルを発生するようになる。ただし、これを有効にすると追加のメモリが必要となる。デフォルト値はNoneで、この場合、追跡管理を有効にする一方で「将来的にはデフォルトで無効化される」という警告を(Flaskアプリ起動時に)表示するようになっている。他の構成キーについては「Configuration Keys」を参照されたい。

 appオブジェクトに構成情報を設定したら、後はそれをSQLAlchemyコンストラクタに渡すだけだ。ただし、ここではデータベースをそもそも作成しておらず、そこに保存するテーブルももちろん作成できていない。だが、これだけでもアプリとしては一応機能する。そこで仮想環境を有効にしたターミナルを開き、「flask shell」コマンドを実行して、試してみよう。「flask shell」コマンドは、環境変数FLASK_APPで指定したアプリのコンテキストで動作するPython対話環境を起動するコマンドだ(環境変数FLASK_APPの設定方法はターミナルの種類による)。以下にコマンドプロンプトをベースとしたターミナルで「flask shell」コマンドを実行したところを示す。

「flask shell」コマンドを実行 「flask shell」コマンドを実行

 上の画像では、REPL環境を起動した後に、「from app import db」を実行して、上に示したコードで定義されているdbオブジェクトをインポートし、それがどんなものかを表示している。「SQLALCHEMY_DATABASE_URI」キーに指定した「sqlite:///db/sample.db」が実際に参照しているファイルがどこにあるかが分かる。なお、上に示した「最小限のアプリ」は単一ファイルで構成されているので、「flask shell」コマンドを使わずとも、単に「python」コマンドでREPL環境を起動して、「from app import db」を実行した瞬間にappモジュールが読み込まれてこのFlaskアプリのコンテキストが作成されるが、アプリが複数ファイルで構成される場合には「flask shell」コマンドを使うようにしよう。

モデルの定義

 次にテーブルに保存する「モデル」を定義してみよう。Flask-SQLAlchemyでは、上で作成したdbオブジェクトが持つメンバ「Model」の派生クラスとして、モデルを定義できる。よって、ここではapp.pyファイルに続けて以下のコードを記述してみよう。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

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

class ToDoItem(db.Model):
  __tablename__ = "todoitems"
  item_id = db.Column(db.Integer, primary_key=True)
  title = db.Column(db.String(100), nullable=False)
  done = db.Column(db.Boolean, nullable=False, default=False)

モデルの定義

 db.Modelは「モデルの基底クラス」であり、さまざまな機能を提供してくれる。__tablename__クラス変数にはこのモデルを保存するテーブルの名前を指定する(指定しなかった場合には、Flask-SQLAlchemyが自動的にテーブル名を決定する)。その後にある3行のクラス変数定義が「このモデルが持つカラム」の定義となる。各メンバは「db.Columnクラスのインスタンス」であり、そのコンストラクタには「カラムの型とその他のオプション」を渡す。

 例えば、item_idクラス変数は「db.Integer」なので整数を保持し、プライマリキーとして機能する。カラムの型としては以下のようなものを指定可能だ(一部)。

  • Integer:整数
  • String(size):文字列(とその最大長)
  • DateTime:日付
  • Float:浮動小数点数
  • Boolean:ブール値

 titleクラス変数は文字列型であり、nullableがFalseなので空の値は許されない(NOT NULL制約)。doneクラス変数はブール値を保持し、nullableがFalseなので空の値は許されず、そのデフォルト値はFalseとなる。

 このようにして定義したモデルを保存するデータベース(とテーブル)を作成するには、dbオブジェクトが持つcreate_allメソッドを呼び出せばよい。といっても、コードでこれを行うのではなく、REPL環境でdbオブジェクトに対してこのメソッドを呼び出すことになる。ここでは「flask shell」コマンドで起動したREPL環境を一度終了して、再度「flask shell」コマンドを実行しておく(コードの追加をアプリのコンテキストに反映するため)。create_allメソッドを呼び出しているところを以下に示す。

create_allメソッドを呼び出してtodoitemsテーブルを作成 create_allメソッドを呼び出してtodoitemsテーブルを作成

データベースの操作

 データベースを作成したら、上で定義したモデルを実際に作成して、それをテーブルに追加できる。REPL環境を開いたままで、次のコードを実行してみよう。

from app import ToDoItem
item1 = ToDoItem(title="buy milk")
item2 = ToDoItem(title="play game!")
db.session.add(item1)
db.session.add(item2)
db.session.commit()

ToDoItemインスタンスの作成とテーブルへの追加

 ここではToDoItemクラスをインポートして、そのインスタンスを2つ作成している。そして、「db.session.add」メソッドを呼び出して、これをテーブルに追加した後に、コミットを行っている。これでデータベースにデータが2つ保存された。追加したデータを読み出すには以下の2つの方法がある。

db.session.query(ToDoItem).all()
ToDoItem.query.all()

全データの読み出し

 前者はデータベースセッションに対してクエリを投げている。後者はToDoItemクラスが持つヘルパー機能を使って同じことをするものだ。これらの行(と追加で幾つかの行)を実行した結果を以下に示す。

データベースの操作 データベースの操作

 特定の条件で取得する項目をフィルタリングするには、filterメソッドやfilter_byメソッドを使う。以下は「item_idが1の項目」を取得するコードをそれぞれの方法で記述したものだ。

ToDoItem.query.filter_by(item_id=1).first()
db.session.query(ToDoItem).filter(ToDoItem.item_id==1).first()

条件でフィルタリング

 filter_byメソッドは「キーワード引数の形式で条件を指定」する。これに対して、filterメソッドはより柔軟な形で条件を記述できる(例えば、item_idの値が100以上といった条件を記述できるのはfilterメソッドだけとなる)。

 データを削除するには、db.session.deleteメソッドに削除したいデータを指定する。以下に例を示す。

db.session.add(ToDoItem(title="foo"))
db.session.commit()
item = db.session.query(ToDoItem).filter_by(title="foo").first()
db.session.delete(item)
db.session.commit()

データの削除

 あるいは、特定の条件に合致するデータを一括して削除するには、filter_by/filterメソッドなどで取得したデータに対してdeleteメソッドを呼び出すことも可能だ。以下に例を示す。

ToDoItem.query.filter(ToDoItem.item_id > 1).delete()

item_idが2以上の項目を一括して削除

 最後にデータの更新は、クエリを用いて取得したデータを変更して、それをaddメソッドで再度追加してもよいが、そのままデータベースをコミットするだけでもよい。以下に例を示す。

# addメソッドを使う例
item = ToDoItem.query.filter_by(title="buy milk").first()
item.done = True
db.session.add(item)
db.session.commit()
for item in ToDoItem.query.all():
  print(item.title, item.done)
# addメソッドは使わなくてもよい
item = ToDoItem.query.filter_by(title="buy milk").first()
item.done = True
db.session.commit()
for item in ToDoItem.query.all():
  print(item.title, item.done)

データの更新

 なお、実際のクエリがどんなものになるかはstr関数を呼び出すと確認できる(ただし、allメソッドや、ここには出ていないが先頭の項目を取得するfirstメソッド呼び出しを付加していると、結果が取り出されてしまうので注意しよう)。例えば、「str(ToDoItem.query)」は「'SELECT todoitems.item_id AS todoitems_item_id, todoitems.title AS todoitems_title, todoitems.done AS todoitems_done \nFROM todoitems'」に展開される。

 Flask-SQLAlchemyを使用したデータベースのCRUD操作の基本もざっくりと見たところで、実際のToDoリストアプリのコードについて見ていくことにしよう。

       1|2 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。