Flask入門|SQLAlchemyでORM操作を学ぼう【チャプター5-04】
一つ前のページではデータベースの操作について学習しました。
今回は SQLAlchemy について見ていきましょう。
Chapter1:Flask入門編
Chapter2:Jinja2入門編
Chapter3:フィルター編
Chapter4:フォーム編
Chapter5:データベース編
・Chapter5-1:データベースとは何か
・Chapter5-2:データベースを作ろう
・Chapter5-3:データベースを操作しよう
・Chapter5-4:SQLAlchemyを使おう ◁今回はここ
・Chapter5-5:内部結合と外部結合を理解しよう
・Chapter5-6:relationshipを理解しよう
・Chapter5-7:Flask-SQLAlchemyを使おう
・Chapter5-8:Flask-Migrateを使おう
Chapter6:Flaskの便利機能編
Chapter7:アプリ開発編
FlaskでWebアプリを開発していると、データを保存したり検索したりする場面が必ず出てきます。
アプリ開発の中でユーザーの情報や投稿内容などを保存・管理するためには、データベースの活用が不可欠です。
しかし、多くの初心者にとって、データベース言語であるSQLを最初から自由自在に書くのはハードルが高いと感じることもあるでしょう。
そこで登場するのが ORM(Object Relational Mapping)という仕組みです。
ORMを使えばSQLを直接書かなくても、Pythonのクラスやメソッドを通じてデータベースを操作できるようになります。
本記事では、Flaskと非常に相性の良いORMライブラリである SQLAlchemy を使って、データベースと連携する基本的な方法を学んでいきます。
- ORMとSQLAlchemyの基本概念
- SQLAlchemyのインストール方法
- データベース接続とモデル定義の方法
- データの登録・取得などの基本操作
- セッションの役割と使い方
初心者の方にも分かりやすいようサンプルコードも交えながら丁寧に解説していきますので、安心して進めていきましょう。
本記事は 有料記事(100円)ですが、現在は期間限定で無料公開中です。
ORMとは?SQLAlchemyとの違いも初心者向けに解説
ORMとは?クラスでデータベースを扱う仕組み
ORM(Object Relational Mapping)とは、「オブジェクトとリレーショナルデータベースの橋渡しをする仕組み」のことです。
通常、リレーショナルデータベース(RDB)ではSQLという言語を使って、データの登録・取得・更新・削除を行います。
しかし、Pythonを含む多くのオブジェクト指向言語では、データはクラスやインスタンスとして扱うのが自然です。
このギャップを埋めてくれるのがORMです。つまり、Pythonのクラスを使ってSQL文を書かずにデータベースを操作できるようになります。
SQLAlchemyとは?Pythonで使えるORMライブラリ
SQLAlchemy は、Pythonで使える代表的なORMライブラリです。
非常に柔軟で高性能なことから、FlaskやDjangoといったWebフレームワークとの連携にもよく使われています。
SQLAlchemyには以下の2つの利用スタイルがあります:
- Core(SQLを直接書く方式)
- ORM(SQLをPythonのクラスで抽象化する方式)
本記事では初心者向けに、後者の「ORMスタイル」に絞って学習を進めます。
SQLAlchemyのインストール方法
SQLAlchemyは外部ライブラリのため、まずはインストールが必要です。
仮想環境を有効化した状態で、以下のコマンドを実行してください:
pip install SQLAlchemy
これでSQLAlchemyを使う準備が整いました。
FlaskとSQLAlchemyの接続設定とエンジン作成方法
ここからは、今回の記事で最終的に出来上がるコードを分割して上から順に紹介していきます。
ぜひ各コードをコピーして、VSCodeに順番に貼り付けながら読んでいきましょう。
モジュールのインポート
データベースを使うには、まずPythonからSQLiteファイルにアクセスするための「接続設定」と、「接続エンジン」を準備する必要があります。
import os # Python標準の「os操作モジュール」をインポート from sqlalchemy import create_engine, Column, Integer, String, or_ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker
この部分では、今回使用するモジュールをインポートしています。
os
:ファイルパスの操作に使います(ファイルのある場所を取得)sqlalch
emycreate_engine
:データベース接続のエンジンを作成Column
,Integer
,String
:テーブル定義に使うクラスdeclarative_base
:ORMで使うモデルの「親クラス」を作成sessionmaker
:データベースとやり取りするための「セッション」生成クラス
なお、SQLAlchemy関連のインポートは覚える必要はありません。
Chapter5-7で学習するFlask-SQLAlchemyでまとめてインポートできるようになります。
データベースファイルの場所を設定
# ==================================== # データベースのパス(保存場所)を設定 # ==================================== base_dir = os.path.dirname(__file__) # 現在のファイルがあるディレクトリのパスを取得して、変数base_dirに代入 database = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite') # base_dir内のdata.sqliteというファイルへのパスを作成し、 # SQLite用の接続文字列(URL形式)を追加して変数databaseに代入
os.path.dirname(__file__)
:このスクリプトファイルが存在するディレクトリのパスを取得します。os.path.join(...)
:そのディレクトリ内にある「data.sqlite」というファイルのパスを作成します。'sqlite:///' + パス
:SQLAlchemy用の「接続文字列」です。sqlite:///〜
はSQLiteに接続するための決まり文句です。
この部分はChapter5-2のコードとほぼ同じです。
接続文字列は「データベースの種類」+「保存先」で構成されており、ここではsqlite:///
を使って「ローカルファイルに保存されたSQLiteに接続する」という設定になっています。
接続エンジンの作成
## データベースエンジンを作成 db_engine = create_engine(database, echo=True) # create_engine関数を使って、DB接続用のエンジンを作成
create_engine()
は、接続エンジンを作るSQLAlchemyの最重要関数のひとつです。- 引数に接続文字列(
sqlite:///...
)を渡すことで、「データベースに接続するためのエンジン(接続窓口)」が作られます。 echo=True
にすると、実際に実行されるSQL文がコンソールに表示されるため、開発中のデバッグに便利です。
ベースクラスの生成
Base = declarative_base() # SQLAlchemyに対応した新しい“親クラス”を生成し、変数Baseに代入 # declarative_baseは「クラスを返す」という特殊な関数
- ORMでは、データベースの「テーブル」をPythonの「クラス」として定義します。
- そのクラスを作るときに共通して継承するのが、ここで作っている
Base
クラスです。 declarative_base()
は、SQLAlchemy専用のクラスファクトリ関数で、テーブル定義の“親クラス”になります。
モデル定義の仕組みとBaseクラスの使い方
SQLAlchemyではデータベース上のテーブルをPythonのクラスで定義します。このとき、先ほど作成した Base
クラスを継承して モデルクラス を作成します。
- モデル = テーブルを作成するクラス
- Baseクラス = モデルの親クラス
今回はItem
という名前のモデルを定義し、「家族の関係(続柄)」と「年齢」の情報を扱うテーブルを作成していきましょう。
id | 続柄 | 年齢 |
1 | 私 | 20 |
2 | 父 | 50 |
3 | 母 | 45 |
# ==================================== # モデル(テーブルを作成するクラス) # ==================================== class Item(Base): # Baseクラスを継承したItemクラスの定義 ## テーブル名 __tablename__ = 'items' ## 番号 id = Column(Integer, primary_key=True, autoincrement=True) ## 続柄 Relationship = Column(String(255), nullable=False, unique=True) ## 年齢 age = Column(Integer, nullable=False) # Columnクラスにてテーブルの列を3つ定義 ## 初期化(__init__メソッド / コンストラクタ) def __init__(self, Relationship, age): self.Relationship = Relationship self.age = age ## 表示用関数 def __str__(self): return F'Item(番号:{self.id}, 関係:{self.Relationship}, 年齢:{self.age})'
モデル定義の基本構造
まず、このようにモデルクラスを定義します:
class モデル名(Base): # Baseクラスを継承したItemクラスの定義 __tablename__ = 'テーブル名' 列1 = Column(データ型, オプション...) ...
__tablename__
:データベース上でのテーブル名を文字列で指定します。Column(...)
:そのテーブルの各列(カラム)を定義します。
各フィールドの詳細解説
1. id
フィールド
## 番号 id = Column(Integer, primary_key=True, autoincrement=True)
Integer
:整数型(数値)primary_key=True
:主キー(各レコードを一意に識別)autoincrement=True
:自動的に連番が振られる
2. Relationship
フィールド
## 続柄 Relationship = Column(String(255), nullable=False, unique=True)
String(255)
:255文字までの文字列nullable=False
:必須項目(空欄を許さない)unique=True
:同じ値は登録できない(重複不可)
3. age
フィールド
## 年齢 age = Column(Integer, nullable=False)
Integer
:整数型(数値)nullable=False
:必須項目(空欄を許さない)
コンストラクタ __init__
と 表示用関数 __str__
__init__
はインスタンスを作るときに、Relationship
とage
を指定して初期化します。__str__
はprint()
などでオブジェクトを表示したときの見た目を決めます。
initメソッドを忘れた人は↓↓の記事を参考にしましょう。
テーブルの作成とセッション設定を理解しよう
テーブル作成
Pythonでモデルを定義しただけでは、まだデータベースにテーブルは作られていません。
定義したクラスに基づいて、実際にSQLiteファイルの中にテーブルを作成するには、次のコードを使います。
# ==================================== # テーブル操作 # ==================================== print('テーブルを作成') Base.metadata.create_all(db_engine) # データベースの作成or接続 & テーブルの作成
Base.metadata.create_all()
の役割
Base.metadata
:これまでに定義したすべてのモデルクラスの情報が格納されています(今回の場合は1つ)。.create_all(エンジン)
:接続先データベースに対して、テーブルを作成します。- すでにテーブルが存在する場合はスキップされるので、重複作成の心配はありません。
- 初回実行時に
data.sqlite
というファイルが作られ、その中にitems
テーブルが作成されます。
セッションの生成 / セッションとは何か
テーブルが作成されたら、次はデータの操作(登録・削除・検索など)を行うための「セッション」を準備します。
## セッションの生成 session_maker = sessionmaker(bind=db_engine) # sessionmakerクラスからセッションファクトリを作成し、変数session_makerに代入 session = session_maker() # session_makerをインスタンス生成し、変数sessionに代入
SQLAlchemyでは セッション(session
)という「一時的な作業空間」を使って、データベースとのやり取りを行います。
sessionmaker()
:セッションを作る“工場”のようなものです。bind=db_engine
:どのデータベースエンジンに接続するかを指定します。session_maker()
:実際にセッション(インスタンス)を生成します。
初期データの削除(リセット)
print('データ初期化:実行') session.query(Item).delete() # session.queryクエリでItemモデルのデータを取得し、delete()メソッドで削除 session.commit() # 上の操作をDBに書き込み・保存
session.query(Item)
:Itemテーブル(items)を対象としたクエリオブジェクトを作成.delete()
:全件削除(WHERE句を指定していないため).commit()
:削除内容をデータベースに反映
このコードはサンプルデータを毎回登録できるように、一旦テーブル内の全データを削除して初期化しているものです。
学習用コードではよく使われる手法です。
SQLを使わずにデータ登録と取得を行う方法(SQLAlchemy)
ここではモデルクラスを使って実際にデータベースにデータを登録する方法を学びます。
SQL文は一切書かず、Pythonのインスタンスを通じて直感的にデータを操作できます。
## データ作成 print('データ登録:実行') item01 = Item('私', 20) # Itemモデル(クラス)をインスタンス生成し、item01に代入 item02 = Item('父', 50) # 同上 item03 = Item('母', 45) # 同上 session.add_all([item01, item02, item03]) # .add_allメソッドにて、リストをテーブルに一括登録 session.commit() # 上の操作をDBに書き込み・保存
モデルのインスタンスを作成
item01 = Item('私', 20) # Itemモデル(クラス)をインスタンス生成し、item01に代入
Item
モデル(テーブル)の1行分のデータを作っている処理です。- コンストラクタ(
__init__
)に値を渡すと、内部でself.Relationship
とself.age
に代入されます。
item02
(父, 50歳)、item03
(母, 45歳)も同じ形式で生成されています。
セッションへの登録とDB反映
session.add_all([item01, item02, item03]) # .add_allメソッドにて、リストをテーブルに一括登録 session.commit() # 上の操作をDBに書き込み・保存
- 複数のインスタンスをまとめて登録しています。
add_all()
の引数にはリストでまとめて渡せます。add()
を使えば、1件ずつ登録することも可能です(例:session.add(item01)
)commit()
を呼び出すことで、登録内容が実際にデータベースに保存されます。
これを行わないと、登録処理は一時的な状態(セッション内だけ)で終わってしまいます。
SQLAlchemy入門のまとめと次のステップへ
ここまでに紹介したコードを全て合わせると以下のようになります。
- コード全体
-
import os # Python標準の「os操作モジュール」をインポート from sqlalchemy import create_engine, Column, Integer, String, or_ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # ==================================== # データベースのパス(保存場所)を設定 # ==================================== base_dir = os.path.dirname(__file__) # 現在のファイルがあるディレクトリのパスを取得して、変数base_dirに代入 database = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite') # base_dir内のdata.sqliteというファイルへのパスを作成し、 # SQLite用の接続文字列(URL形式)を追加して変数databaseに代入 ## データベースエンジンを作成 db_engine = create_engine(database, echo=True) # create_engine関数を使って、DB接続用のエンジンを作成 Base = declarative_base() # SQLAlchemyに対応した新しい“親クラス”を生成し、変数Baseに代入 # declarative_baseは「クラスを返す」という特殊な関数 # ==================================== # モデル(テーブルを作成するクラス) # ==================================== class Item(Base): # Baseクラスを継承したItemクラスの定義 ## テーブル名 __tablename__ = 'items' ## 番号 id = Column(Integer, primary_key=True, autoincrement=True) ## 続柄 Relationship = Column(String(255), nullable=False, unique=True) ## 年齢 age = Column(Integer, nullable=False) # Columnクラスにてテーブルの列を3つ定義 ## 初期化(__init__メソッド / コンストラクタ) def __init__(self, Relationship, age): self.Relationship = Relationship self.age = age ## 表示用関数 def __str__(self): return F'Item(番号:{self.id}, 関係:{self.Relationship}, 年齢:{self.age})' # ==================================== # テーブル操作 # ==================================== print('テーブルを作成') Base.metadata.create_all(db_engine) # データベースの作成or接続 & テーブルの作成 ## セッションの生成 session_maker = sessionmaker(bind=db_engine) # sessionmakerクラスからセッションファクトリを作成し、変数session_makerに代入 session = session_maker() # session_makerをインスタンス生成し、変数sessionに代入 print('データ初期化:実行') session.query(Item).delete() # session.queryクエリでItemモデルのデータを取得し、delete()メソッドで削除 session.commit() # 上の操作をDBに書き込み・保存 ## データ作成 print('データ登録:実行') item01 = Item('私', 20) # Itemモデル(クラス)をインスタンス生成し、item01に代入 item02 = Item('父', 50) # 同上 item03 = Item('母', 45) # 同上 session.add_all([item01, item02, item03]) # .add_allメソッドにて、リストをテーブルに一括登録 session.commit() # 上の操作をDBに書き込み・保存 print('データ参照:実行') item_all_list = session.query(Item).order_by(Item.id).all() # Itemモデルに基づく全データを、idの昇順で取得し、リストとして返す for row in item_all_list: print(row)
SQLite Viewer を使用して作成される表を確認してみましょう。
今回は、SQLを直接書かなくてもPythonコードだけでデータベース操作ができる便利な仕組み、ORMと、Pythonで代表的なORMライブラリであるSQLAlchemyについて学びました。
学習した項目 | 内容 |
---|---|
ORMの基本 | クラスでテーブルを表現し、直感的に操作できる手法 |
SQLAlchemyの導入 | インストールと接続設定方法、エンジンの作成 |
モデル定義 | テーブル構造をPythonのクラスで表現 |
データ操作 | 登録・削除・取得などをセッションを通して実行 |
これらのステップを理解すれば、Flaskを使った実用的なWebアプリ開発の土台がしっかりと築けます。
SQLの難しい文法にとらわれることなく、Pythonらしい書き方でデータベースを自在に扱えるのは、非常に大きな武器になります。
最初は少しコードの量に圧倒されるかもしれませんが、心配はいりません。
一つ一つの処理はとても論理的で、慣れれば自然に読み書きできるようになります。何度も実行しながら理解を深めていきましょう。
次回は、さらに一歩進んで「テーブルの結合(JOIN)」について学びます。
データ同士のつながりを扱えるようになることで、アプリの表現力は格段にアップします。
練習問題|セッションとクエリでデータ更新を体験しよう
この問題では、上記の解説部分で作成したコードの末尾に追加でコードを書き、すでに登録されているデータの一部を更新する処理を実装します。
データベースには3件の家族データが保存されており、それぞれ id=1
〜id=3
のレコードとして登録されています。
そのうち、id=3
のレコード(母)と、id=1
とid=2
のレコード(私と父)の年齢を上書きする処理を追加してください。
更新処理には、SQLAlchemyのfilter()
やor_()
を使用し、条件に合致したレコードを選んでから値を変更し、session.commit()
で反映させます。
この問題の要件
以下の要件に従ってコードを完成させてください。
id=3
のレコードを1件取得し、そのage
を40
に書き換えることid=1
またはid=2
のレコードを取得し、それぞれのage
を20
に書き換えること- いずれも、
session.query()
によってレコードを取得すること - 複数条件の取得には
or_()
を使用すること - すべての更新内容を
session.commit()
を使ってデータベースに反映させること - 処理の直前に
print()
文で処理名(例:「データ更新:実行」など)を出力すること
この問題を解くヒント
1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。
- ヒント:コードの構成を見る
-
正解のコードは上から順に以下のような構成となっています。
- 「idが3番のレコードを1件だけ取得」するためのコードを書く(クエリで条件指定を行う)。
- 取得したレコードの年齢(age)を「40」に書き換える。
- データベースに変更を反映するために、コミットの処理を書く。
- 「idが1または2のレコードをまとめて取得」するためのクエリを書く(or条件を使う)。
- 取得した複数レコードをfor文で1件ずつ処理し、それぞれの年齢(age)を「20」に変更する。
- この一連の変更をデータベースに保存するため、再度コミットする処理を書く。
- 各更新処理の直前に、
print()
で「どの処理をしているのか」を画面に表示する行を追加する。
このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。
正解コード
例えば、以下のようなプログラムが考えられます。
- 正解コード
-
print('データ更新1件:実行') target_item = session.query(Item).filter(Item.id==3).first() # Item テーブルから id == 3 のレコードを1件取得 target_item.age = 40 # ageに40を再代入 session.commit() # 上の操作をDBに書き込み・保存 print('データ更新複数件:実行') target_item_list = session.query(Item).filter(or_(Item.id==1, Item.id==2)).all() for target_item in target_item_list: target_item.age = 20 # 全てのageに20を再代入 session.commit() # 上の操作をDBに書き込み・保存
正解コードの詳細解説
正解コードをブロックごとに分割して解説します。
- 正解コードの詳細解説
-
1件のデータを更新する処理
print('データ更新1件:実行') target_item = session.query(Item).filter(Item.id==3).first() # Item テーブルから id == 3 のレコードを1件取得 target_item.age = 40 # ageに40を再代入 session.commit() # 上の操作をDBに書き込み・保存
print('データ更新1件:実行')
→ 処理が始まることをコンソールに表示するための出力文です。session.query(Item)
→ Itemテーブル(モデル)に対してデータを検索する準備をします。.filter(Item.id == 3)
→idが3のレコード
だけを対象に絞り込みます。.first()
→ 絞り込んだ結果の中から最初の1件だけを取り出します。target_item
という変数にそのレコードが入ります。target_item.age = 40
→ 取得したデータのage
を40に書き換えます。session.commit()
→ 変更した内容を実際にデータベースに保存します。
複数のデータを一括で更新する処理
print('データ更新複数件:実行') target_item_list = session.query(Item).filter(or_(Item.id==1, Item.id==2)).all() for target_item in target_item_list: target_item.age = 20 # 全てのageに20を再代入 session.commit() # 上の操作をDBに書き込み・保存
print('データ更新複数件:実行')
→ 複数データの更新処理を開始することを示す出力文です。session.query(Item)
→ こちらもItemテーブルに対してデータ検索の準備をします。.filter(or_(Item.id == 1, Item.id == 2))
→idが1または2のレコード
を条件にして絞り込みます。or_()
は、複数の条件のいずれかに当てはまるデータを選びたいときに使います。.all()
→ 該当する全てのレコードをリストとして取得します。target_item_list
に格納されます。for target_item in target_item_list:
→ 取得したデータを1件ずつ順番に処理していくループです。target_item.age = 20
→ 各レコードのage
を20に上書きします。session.commit()
→ すべての変更を一括で保存します。
補足:使われている主な文法・構文
文法・関数 説明 query()
データベースからデータを検索するためのメソッド filter()
条件を指定してデータを絞り込む or_()
2つ以上の条件のどれかを満たす場合に使用 first()
結果の最初の1件を取得 all()
結果を全件取得(リストとして) commit()
データベースに変更を保存する命令
FAQ|SQLAlchemyの基本操作とORMの疑問を解消
- Q1. SQLAlchemyでデータベースに接続するには何が必要ですか?
-
SQLAlchemyでは、
create_engine()
関数を使ってデータベースの「接続エンジン」を作成する必要があります。接続文字列(例:sqlite:///data.sqlite
)を指定するだけで、簡単にSQLiteなどに接続できます。
- Q2. セッションって何のために使うのですか?
-
セッションは、一時的にデータベースとやり取りするための作業空間です。変更操作はまずセッションに保存され、
commit()
を実行して初めてデータベースに反映されます。安全なトランザクション管理が可能になります。
- Q3. ORMを使うとSQLは一切書かなくてよくなるのですか?
-
基本的な操作(登録・更新・取得など)はORMだけで可能です。ただし、複雑なクエリやパフォーマンス重視の場面では、SQLAlchemyの「Core」機能を使って直接SQLを書くこともできます。