はじめに
最近はPythonを書いてます。どうも僕です。
PythonでDBを扱う際、SQLAlchemy
というORMが選択肢に入ってくるかと思います。
今回はこいつをある程度触った感想なりを書いていきます。
感想
使ってみた所感を。
「んーーーー微妙。。。。」
といった感じ。
(なんかいろいろ言われそう怖い…)
なにが微妙な点なのか。
もちろん、いい点もあり、なんとも言えない部分もあります
基本的な使い方
SQLAlchemyでは、DBへの接続をsession
として扱い、クエリを実行します。
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
# mysql の設定
DATABASE = 'mysql://%s:%s@%s:%s/%s?charset=utf8' % (
"認証情報"
)
ENGINE = create_engine(
DATABASE,
encoding = "utf-8",
echo = False
)
# session の作成
session = scoped_session(
sessionmaker(
autocommit=False,
autoflush=False,
bind = ENGINE
)
)
# model で使用する
Base = declarative_base()
Base.query = session.query_property()
このsession
を呼び出して、SQLを実行します。
また、テーブルもPython側で定義します。
class Test(Base): # ここのBaseはsessionのとこで定義したBase
__tablename__ = "testTable"
id = Column("id", Integer, primary_key=True)
text = Column("text", String(127), nullable=False)
created = Column("created_at", DATETIME, default=datetime.now, nullable=False)
updated = Column("updated_at", TIMESTAMP, default=datetime.now, onupdate=datetime.now, nullable=False)
これらを呼び出して処理を行う。
selectとinsertしてみる
select
# idが一致するものの最初のデータを取得
def selectOne(id, session):
data = session.query(Test).filter_by(id = id).first()
return data
insert
def insert(id, text, session):
data = Test()
data.id = id
data.text = text
session.add(data)
session.commit()
APIから呼び出す時の書き方
APIを用いてDBにCRUDする際の関数の書き方について、僕のたどり着いた書き方を紹介します。
ex) APIに投げた値をTestテーブルに書き込む
@app.route("/test", methods=["GET"])
def testFunc():
data = request.get_json()
insertTest(data['id'], data['text'])
return jsonify({"result": "success"})
def insertTest(id, text):
try:
session = <sessionを定義したところから持ってくる>
db.Test().insert(id,text,session)
except Exception as error:
logger.error("err --> %s", str(error))
session.rollback()
abort(400, "error")
finally:
session.close()
class Test():
def insert(self, id, text, session):
test = Test()
test.id = id
test.text = text
session.add(problem)
session.commit()
return
このような流れになります。
処理フローとしては、
-
/test
にGETリクエストが来る -
insertTest
関数が呼び出される -
class Test
のinsert
関数が呼び出される - insert処理が走る
-
commit
して終了
ポイントとしては、insertTest
関数です
いちいちinsertの処理を別関数を呼んでます。
これはいくつかのメリットがあるからです
- テーブルごとにクラスを割り振ることで、わかりやすくするため
-
try
を用いることで処理が失敗した際のロールバックが可能 - DBに対する処理を同じセッションで管理できる⇨重要
同じセッションで管理できるのが大事で、例えば二つのテーブルに値を挿入する時、片方のテーブルは成功したけど、片方では失敗した場合。
同じセッションで管理していたので、ロールバック処理が可能です。これが関数を分けるメリットです。
微妙な点
こんなコードがあったとします。
def qiitaFunc(self):
session = module.DBSetting.session
testData = session.query(Test).filter_by(id = int(id)).first()
print(testData)
session.close
これはTest
テーブルにidでセレクトをして、それの一番最初にヒットするデータを持ってくる関数ですね
では、これの出力結果を見ましょう。
<Test object at 0x7f70ce0c4cc0>
そう、これの出力結果はobject at hogehoge
の形式で出てきます。
こいつは辞書の形式?なのかな
とりあえず、
print(testData.id)
というふうに、パラメータを指定しないと出力されません。
例えば、Go言語のDBライブラリのGorm
とかだと、構造体を定義してそれに当てはめたりするので、このようなことは起きません。
僕個人的には、データがちゃんときているか、というのを確認するため、しょっちゅう標準出力するので、この仕様はストレスでしかなかったです。
以上が、微妙だなと思った点です。
総括
まぁ先ほどの面倒な出力問題に目を閉じれば結構使いやついものだなと思いました。
あと、SQLインジェクションも変なことしない限り大丈夫みたいな文献も見たので、その辺もいいと思う点ですね。
classを用いてどのテーブルになにしてるかを簡潔にできるのはいい点だなと思います。
では。