nikkie-ftnextの日記

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

SQLModel素振りの記:Sessionにaddしてcommitした後、addしたデータにアクセスするにはrefreshまたは属性アクセス

はじめに

みんな、手うがしよう! nikkieです。

SQLModelでデータをINSERTした後の扱い方を知りました。

目次

PyCon JP 2024より「SQLModel入門」

FastAPIの作者(tiangolo氏)が作ったORM、SQLModel。

日本語で読める資料としては、みずきさんによる「SQLModel入門」がオススメです。

まずこちらを通読し、その後公式のチュートリアルに取り組みました。
その中で知ったことを綴ります。

トピックとしては「基本的なデータの作成方法」(スライド47)

sessionにデータをaddしてcommitした後、データにアクセスすると(printする例)

内部的に期限切れとして認識されており、refreshされるまではNoneが返される。(スライド46

スライド47ではcommitした後にrefreshすることでデータにアクセスできると説明されます。

with Session(engine) as session:
    session.add(hero_1)
    session.commit()
    print("Hero1: ", hero_1)  # hero_1はNoneとして評価される
    session.refresh(hero_1)
    print("Hero1: ", hero_1)  # hero_1はデータを指す

公式チュートリアル「Automatic IDs, None Defaults, and Refreshing Data」

公式チュートリアルSQLiteを使って進みます1
途中にあった「テーブルの作成はスクリプト2」というトピックが興味深かったですが、この記事ではテーブルができた後のデータのINSERTを扱います。

「Automatic IDs, None Defaults, and Refreshing Data」で、みずきさん資料と同様のコードが出てきます。

Heroインスタンスsessionaddしてcommitした後、
https://sqlmodel.tiangolo.com/tutorial/automatic-id-none-refresh/#commit-the-changes-to-the-database

And now, something unexpected happens, look at the output, it seems as if the Hero instance objects had no data at all:

Heroインスタンスを指していた変数はNoneとして評価されています(automatic expiration)。
属性にアクセスすることでrefreshされると続きます

By accessing the attribute, that triggers a lot of work done by SQLModel (actually SQLAlchemy) underneath to refresh the data from the database, set it in the object's id attribute,

公式チュートリアルをベースにしたスクリプトを動かして確認しました。

uv runなどPEP 723(inline script metadata)をサポートしたツールで動かしています(SQLModelはバージョン 0.0.22)

echo=Trueによる出力を見ても

  • After commit:の前はDBへのアクセスはなし
  • After commit (access id):の前、SELECT文が発行されている
SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?

?には(1,)と渡るようです。
hero_1id1というのは、どこが知っている情報なんだろう?)

終わりに

SQLModelでSessionを使ってデータをINSERTした時のauto expirationについて見てきました。

with Session(engine) as session:
    session.add(hero_1)
    session.commit()  # auto expiration (hero_1自体を評価するとNone)

    # hero_1の属性にアクセスするか、sessionをrefreshする
    hero_1.id
    session.refresh(hero_1)

session.commit()Sessionaddした変数の指すデータが変わって見えるのは副作用っぽさもあり独特だなと思います(クセが強いとこかも)。
属性アクセスまたは明示的なSessionrefreshするという扱い方を完全理解です!