目次

目次

SQLAlchemyが生成するクエリを確認する

松木佑徒
松木佑徒
最終更新日2017/06/19 投稿日2017/06/19

WIZY の開発では、Python + MySQLの構成でORマッパーに SQLAlchemy を使用しています。 SQLAlchemyは高機能なのですがまとまったドキュメントがなく、また「どのようにコードを記述したらどのようなクエリが発行されるのか」というのはDB設計やユースケースがサンプルとは異なるので、運良くマッチした情報が出て来ない限り自分でクエリ(を生成するコード)を組み立てる必要があります。

パターン1) loggingでログに出力する

SQLAlchemyを利用しているアプリケーションで以下のコードを記述することでクエリをログに出力することができます。

import logging

logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)

パターン2) mysql用にクエリをコンパイル

パターン1はクエリに渡した変数が出力されない問題があります。 変数を含むクエリ全体を確認したい場合はクエリをコンパイルして確認する方法が使えます。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.dialects import mysql

class MyTable(declarative_base()):
    __tablename__ = 'my_table'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(255, collation='utf8_unicode_ci'), default=None)

if __name__ == '__main__':
    Session = sessionmaker()
    query = Session().query(MyTable).filter(MyTable.id == 10)
    print(query.statement.compile(dialect=mysql.dialect(),
                                  compile_kwargs={"literal_binds": True}))

実行結果

SELECT my_table.id, my_table.name 
FROM my_table 
WHERE my_table.id = 10

コンパイルに渡している dialect でMySQLの文法で変換すること、 compile_kwargs={"literal_binds": True} で変数をセットした状態で表示することを示しています。

Flask-SQLAlchemyを使用した場合

WebフレームワークFlaskとSQLAlchemyを利用する場合はFlask-SQLAlchemyが利用できます。 この場合に同じことをするには、Flask-Scriptも使用して以下のようになります。

from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.dialects import mysql

app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy()
db.init_app(app)
manager = Manager(app)

class MyTable(db.Model):
    __tablename__ = 'my_table'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(255, collation='utf8_unicode_ci'), default=None)

@manager.command
def main():
    query = db.session.query(MyTable).filter(MyTable.id == 10)
    print(query.statement.compile(dialect=mysql.dialect(),
                                  compile_kwargs={"literal_binds": True}))

if __name__ == '__main__':
    manager.run()

実行結果

$ python hoge.py main
SELECT my_table.id, my_table.name 
FROM my_table 
WHERE my_table.id = 10

まとめ

ORマッパーを使用していても複雑な条件を利用したい場合は実際に出力されるSQLを確認しながら開発を進めることで、予想外の動きやケース漏れを防ぐことができると思います。

松木佑徒

目次