0
0

More than 1 year has passed since last update.

【ORM】SQLAlchemyを使ってみた感想

Posted at

はじめに

最近はPythonを書いてます。どうも僕です。

PythonでDBを扱う際、SQLAlchemyというORMが選択肢に入ってくるかと思います。

今回はこいつをある程度触った感想なりを書いていきます。

感想

使ってみた所感を。

「んーーーー微妙。。。。」

といった感じ。
(なんかいろいろ言われそう怖い…)

なにが微妙な点なのか。

もちろん、いい点もあり、なんとも言えない部分もあります

基本的な使い方

SQLAlchemyでは、DBへの接続をsessionとして扱い、クエリを実行します。

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テーブルに書き込む

main.py
@app.route("/test", methods=["GET"])
def testFunc():
    data = request.get_json()
    insertTest(data['id'], data['text'])
    return jsonify({"result": "success"}) 
handler.py
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()
controller.py
class Test():
    def insert(self, id, text, session):
        test = Test()
        test.id = id
        test.text = text
        session.add(problem)
        session.commit()
        return

このような流れになります。
処理フローとしては、

  1. /testにGETリクエストが来る
  2. insertTest関数が呼び出される
  3. class Testinsert関数が呼び出される
  4. insert処理が走る
  5. commit して終了

ポイントとしては、insertTest関数です
いちいちinsertの処理を別関数を呼んでます。

これはいくつかのメリットがあるからです

  1.  テーブルごとにクラスを割り振ることで、わかりやすくするため
  2.  tryを用いることで処理が失敗した際のロールバックが可能
  3. 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を用いてどのテーブルになにしてるかを簡潔にできるのはいい点だなと思います。

では。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0