はじめに
SQLAlchemyでは,sessionを生成したあと,必要に応じてcommit()
やrollback()
,close()
を行う必要がある.
ここでは,DB操作を行うクラスを作成し,sessionの受け渡し方についてまとめる.
以下では,下のようなモデルクラスが存在しているとする.
class User(Base):
__tablename__ = "user" # テーブル名を指定
id = Column(Integer, primary_key=True)
first_name = Column(String(255))
last_name = Column(String(255))
sessionの悪い扱い方
以下のソースコードのようにしてしまうと良くない.
from models import User
class FirstName(object):
def update_first_name(self, user_id, first_name):
session = Session()
try:
user = session.query(User).filter(
User.id == user_id).one() # id=user_idであるobjを取り出す
user.first_name = first_name # first_nameを変更
session.commit()
except:
session.rollback()
raise
class LastName(object):
def update_last_name(self, user_id, last_name):
session = Session()
try:
user = session.query(User).filter(
User.id == user_id).one() # id=user_idであるobjを取り出す
user.first_name = first_name # last_nameを変更
session.commit()
except:
session.rollback()
raise
def run_my_program():
FirstName().update_first_name(1, "update_first_name")
LastName().update_last_name(1, "update_last_name")
なぜなら,run_my_program
中のupdate_first_name
とupdate_last_name
で,同一sessionが使われていないため,
「first_nameはupdateされたが,last_nameはupdateされなかった」
のようなことが起きてしまうからである.
sessionの良い扱い方1
そこで,以下のソースコードのようにすれば解決する.
from models import User
class FirstName(object):
def update_first_name(self, user_id, first_name, session):
user = session.query(User).filter(User.id == user_id).one()
user.first_name = first_name
class LastName(object):
def update_last_name(self, user_id, last_name, session):
user = session.query(User).filter(User.id == user_id).one()
user.first_name = first_name
def run_my_program():
session = Session()
try:
FirstName().update_first_name(session)
LastName().update_last_name(session)
session.commit()
except:
session.rollback()
raise
finally:
session.close()
こうすることで,update_first_name
とupdate_last_name
で,同一sessionが使われ,
「片方だけが正常に実行された」
のような状況が起こらない.
sessionの良い扱い方2
次に,context manager
を用いたsessionの扱い方を紹介する.
なお,context managerについては,以下の記事が大変わかりやすく参考になるのでここでは説明を省略する.
Pythonのコンテキストマネージャってなんなの?と思って調べた話
from models import User
from contextlib import contextmanager
@contextmanager
def session_scope():
session = Session() # def __enter__
try:
yield session # with asでsessionを渡す
session.commit() # 何も起こらなければcommit()
except:
session.rollback() # errorが起こればrollback()
raise
finally:
session.close() # どちらにせよ最終的にはclose()
class FirstName(object):
def update_first_name(self, user_id, first_name, session):
user = session.query(User).filter(User.id == user_id).one()
user.first_name = first_name
class LastName(object):
def update_last_name(self, user_id, last_name, session):
user = session.query(User).filter(User.id == user_id).one()
user.first_name = first_name
def run_my_program():
with session_scope() as session:
FirstName().update_first_name(session)
LastName().update_last_name(session)
参考文献
この記事は以下の情報を参考にして執筆しました.
・公式ドキュメント(Session Basics)
・Pythonのコンテキストマネージャってなんなの?と思って調べた話