nikkie-ftnextの日記

イベントレポートや読書メモを発信

SQLModel素振りの記:PostgreSQLに同期接続(SQLModelのSessionと、SQLAlchemyのsessionmaker)

はじめに

ホワイトデーにはアリをお返し🐜 nikkieです。

FastAPIで使うSQLModelの素振りです。
SQLAlchemyの知識も必要になってくるのですが、どちらも今はまだ初学者レベルですので、考え違いをしていたらやさしく教えてください!

目次

動作環境

dockerコンテナでPostgreSQLを立ち上げます。
macOSでRancher Desktopを使っています。

% docker --version
Docker version 27.5.0-rd, build 7a37716
% docker run --rm --name postgres \
    -e POSTGRES_USER=developer \
    -e POSTGRES_PASSWORD=mysecretpassword \
    -e POSTGRES_DB=practice \
    -p 5432:5432 -d postgres:15.10

DBに接続した後は、以前のスクリプトの内容でINSERTします(Hero 1人を登録!)。
SQLiteで動かしたスクリプトがこちらにあります。

各種バージョン

  • Python 3.12.8
  • sqlmodel 0.0.22
  • SQLAlchemy 2.0.38
  • psycopg2 2.9.10
    • SQLAlchemy[postgresql]というextra指定で入ります1

SQLModelドキュメントのやり方(Session)

sqlmodel.Sessionを使うやり方です。
sqlalchemy.orm.Sessionを加工したクラスですね2

PostgreSQLへの接続まわりは

  1. DBの接続URLを組み立て
    • このスクリプトは練習なのでハードコードしましたが、アプリケーションを作る際は環境変数から読むなどしたいですね
  2. create_engine()
    • この実体はsqlalchemy.engine.create_engine()です3。何も変えていません
  3. insertする際はengineからSessionを作成

実行結果4

Before insert: name='Deadpond' secret_name='Dive Wilson' id=None age=None
After add: name='Deadpond' secret_name='Dive Wilson' id=None age=None
After commit: 
After commit (access id): 1
After id access: age=None secret_name='Dive Wilson' id=1 name='Deadpond'
After session close: age=None secret_name='Dive Wilson' id=1 name='Deadpond'

SQLAlchemyのsessionmakerを試す

SQLModelのドキュメントから外に目を向けると、sessionmakerなるものの存在を知りました5

+from sqlalchemy.orm import sessionmaker
-from sqlmodel import Field, Session, SQLModel, create_engine
+from sqlmodel import Field, SQLModel, create_engine

# 省略

database_url = "postgresql://developer:mysecretpassword@127.0.0.1:5432/practice"
engine = create_engine(database_url)
+Session = sessionmaker(engine)

if __name__ == "__main__":
     # 省略

+    with Session(engine) as session:
-    with Session() as session:

全容はこちらでどうぞ
https://gist.github.com/ftnext/554c6365f1232f153ff517c89b911066

実行結果

Before insert: name='Deadpond' secret_name='Dive Wilson' id=None age=None
After add: name='Deadpond' secret_name='Dive Wilson' id=None age=None
After commit: 
After commit (access id): 2
After id access: secret_name='Dive Wilson' id=2 name='Deadpond' age=None
After session close: secret_name='Dive Wilson' id=2 name='Deadpond' age=None

SQLAlchemyのドキュメントから
「Basics of Using a Session」の中の「Using a sessionmaker

The purpose of sessionmaker is to provide a factory for Session objects with a fixed configuration.

sessionmakerSessionオブジェクトのファクトリを提供(※端折った訳)

sessionmaker()の返り値Session(ファクトリ)を呼び出すと、(SQLAlchemyの)Sessionインスタンスが返るわけですね。

sessionmakerのAPIドキュメント
https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.sessionmaker
__call__()Sessionインスタンスを返しています

終わりに

SQLModelからPostgreSQLのDBに同期接続するにあたり、2つの方法を見ました。

  • SQLModelドキュメントのSession(engine)
  • SQLAlchemyのsessionmakerを使うやり方(sessionmaker(engine)()

SQLAlchemyのsessionmakerの返り値のファクトリは、ドキュメントでまだ読み切れていない箇所があります6
引き続き読んでいきます


  1. https://github.com/sqlalchemy/sqlalchemy/blob/rel_2_0_38/setup.cfg#L63
  2. exec()メソッドを生やしています https://github.com/fastapi/sqlmodel/blob/0.0.22/sqlmodel/orm/session.py#L27
  3. https://github.com/fastapi/sqlmodel/blob/0.0.22/sqlmodel/__init__.py#L4
  4. ロギングはこちらから (echo=TrueするとコンソールにSQLもprintも出るので、SQLをファイルにロギングしています)
  5. きっかけはrhoboroさんのリポジトリです。 https://github.com/rhoboro/async-fastapi-sqlalchemy/blob/12683934b451c76af5eea7ee5bf4e6c8342ca165/app/db.py#L17 私には初見なので、まずは同期版のsessionmakerから押さえることにしました
  6. sessionmaker(engine).begin()というやり方もできるような...