MySQL-Pythonの正しい使い方
img.azyobuzi.net の開発が予想以上に遅れてます、 azyobuzin です。本当は、 img.azyobuzi.net の新バージョンを完成させてから書こうと思っていた記事ですが、諦めて書いてしまいます。
さて、今日は個人的に今まで間違った(?)使い方をしていた MySQL-Python(以下 MySQLdb)のお話をしようと思います。
今までの書き方
昔の img.azyobuzi.net や TbrFeed のソースコードを見ればわかると思いますが、こんな書き方をしていました。
import MySQLdb #接続 db = MySQLdb.connect(user="hoge", passwd="bar", db="booboo", charset="utf8") c = db.cursor() #INSERT c.execute("INSERT INTO %s (id, username, token, secret, created, lastaccess) VALUES (%s, %s, %s, %s, NOW(), NOW()) ON DUPLICATE KEY UPDATE token = %s, secret = %s, created = NOW(), lastaccess = NOW()" % (usersTable, db.literal(id), db.literal(username), db.literal(oauthToken), db.literal(oauthTokenSecret), db.literal(oauthToken), db.literal(oauthTokenSecret))) db.commit() c.close() db.close()
db.literal が大量にあってウザいですね。さて、ここから無駄なところを削り、清潔な Python コードにしていきます。
1. with で接続を管理
まず、 with の挙動を確認していきます。 with 文は __enter__()
を呼び出して、その引数を as のあとの変数にいれ、ブロックの最後に __exit__(self, exc, value, tb)
を呼び出して終了します。そして、 MySQLdb の Connection の実装では、
def __enter__(self): return self.cursor() def __exit__(self, exc, value, tb): if exc: self.rollback() else: self.commit()
となっています。
つまり、
with MySQLdb.connect(user="hoge", passwd="bar", db="booboo", charset="utf8") as c: #ポイント: c は Cursor のインスタンスです。間違えやすい。 pass
としてやれば、接続後、勝手に Cursor を取得し、 with 文終了時点で勝手にコミットしてくれるので、 close()
も commit()
も必要ないわけですね!*1
2. Connection.literal にさよならバイバイ
最初のコードに、 db.literal(o)
がたくさん出て来ましたが、そもそもこれ、ドキュメントには
Non-standard. For internal use; do not use this in your applications.
MySQLdb API Documentation
と書かれています。
では、どうやってエスケープするのか?答えは Cursor.execute(query, args)
です。内部には
query = query % db.literal(args)
というコードがあります。つまり、 query を文字列フォーマットで指定しておき、 args にタプルを指定すれば、自動的にエスケープされた文字列が挿入されるようです。
まとめ
最初のコードはこんな感じに書きなおすことができます。
import MySQLdb #接続 with MySQLdb.connect(user="hoge", passwd="bar", db="booboo", charset="utf8") as c #INSERT c.execute("INSERT INTO " + usersTable + " (id, username, token, secret, created, lastaccess) VALUES (%s, %s, %s, %s, NOW(), NOW()) ON DUPLICATE KEY UPDATE token = %s, secret = %s, created = NOW(), lastaccess = NOW()", (id, username, oauthToken, oauthTokenSecret, oauthToken, oauthTokenSecret))
ちょっと MySQLdb を見直してしまった。