SQLAlchemy 2.xでSELECTを書くと、
select(User)
session.execute(...)
session.scalars(...)
と似た処理がいくつも登場します。
最初は「結局どれを使えばいいの?」と迷いましたが、それぞれ役割が違います。
select()はSQLを組み立てる
まず、
stmt = select(User)
の時点では、まだDBからデータを取得していません。
select()は、
どのデータを取得するか
を表すSELECT文を組み立てています。
条件を付ける場合も同じです。
stmt = select(User).where(
User.id == user_id
)
これをSessionへ渡して、初めてクエリを実行します。
ORMモデルならscalars()が分かりやすい
UserのようなORMオブジェクトを取得したい場合は、scalars()を使うとシンプルです。
result = await session.scalars(
select(User)
)
users = result.all()
session.scalars()はScalarResultを返し、そこからUserオブジェクトを直接取得できます。
select(User)
↓
session.scalars()
↓
ScalarResult
↓
Userオブジェクト
SQLAlchemy公式でも、ORM Entityを取得する例ではこの形が使われています。
execute()ではResultが返る
一方、
result = await session.execute(
select(User)
)
ではResultが返ります。
ORMモデルだけ欲しい場合は、
users = result.scalars().all()
と、さらにscalars()でORMオブジェクトを取り出します。
つまり、
result = await session.scalars(select(User))
は、ORM Entityを取得する用途では次の書き方を簡潔にしたものと考えられます。
result = await session.execute(select(User))
users = result.scalars()
execute()は複数カラム取得でも使う
例えば、
stmt = select(
User.id,
User.name,
)
result = await session.execute(stmt)
rows = result.all()
のように複数カラムをSELECTすると、取得したいのはUserオブジェクトそのものではありません。
この場合はexecute()でRowとして扱う方が自然です。
まずは、
ORMモデルを取得
→ scalars()
複数カラムなどを取得
→ execute()
と整理すると分かりやすいです。
session.query()はなくなった?
SQLAlchemy 1.xでは、
session.query(User)
という書き方がよく使われていました。
SQLAlchemy 2.xでもQuery API自体は残っていますが、現在はLegacy APIという位置付けです。
新しく書く場合は、
select(User)
とSession.execute() / Session.scalars()を組み合わせる2.xスタイルを基本にするとよいでしょう。
まとめ
-
select():SELECT文を組み立てる -
scalars():ORMオブジェクトを直接扱いたいときに便利 -
execute():Resultを取得する。複数カラムなどにも使える -
execute(...).scalars()とscalars(...)はORM Entity取得では同じ流れ -
session.query()は削除されていないがLegacy API
まずは**「ORMモデルが欲しいならsession.scalars(select(...))」**を基本形として覚えると、SQLAlchemy 2.xのSELECTがかなり読みやすくなります。