読者です 読者をやめる 読者になる 読者になる

MySQLdbのcharset

Python SQLAlchemy

 SQLAlchemyで、MySQLのクライアント・エンコーディングを指定するには、create_engine()に与えるDSNにcharset=encodingのパラメータを与えます。

from sqlalchemy import *

engine = create_engine('mysql://username:password@localhost/dbname?charset=utf8')


 MySQLdbはcharsetを明示的に指定した場合は、結果セットの文字列をすべてユニコードで返すので、必然的にSQLAlchemyが返す結果も、ユニコード文字列になります。(SQLAlchemy側にも、create_engine()のオプションとして、convert_encodingという設定があるので、今後、別のDBでこの挙動を確かめてみようと思っています。)

>>> from sqlalchmey import *
>>> md = BoundMetaData("mysql://user:password@localhost/dbname?charset=utf8")
>>> cats = Table("cat", md, autoload=True)
>>> cats.select().execute().fetchone()
(2L, u'\u30a4\u30c3\u30d1\u30a4\u30a2\u30c3\u30c6\u30ca', 0)

# VARCHAR型の列の結果はユニコードで得られる


 charsetを指定すれば、挿入や更新時にMySQLdbがユニコード文字列を、データベースの文字セットにエンコードしてくれます。

# catsはTableオブジェクトのインスタンス
stmt = cats.update(
    values={cats.c.name:bindparam('name')},
    whereclause=cats.c.id==bindparam('id')
).compie()

# 実行&コミット
stmt.execute(name=u'ブッチャー', id=4)


 SqlSoupを使った場合は、次のようになります。

from sqlalchemy.ext.sqlsoup import SqlSoup

db = SqlSoup("mysql://user:password@localhost/dbname?charset=utf8")

# cはMappedObjectのインスタンス
c = db.cat.get_by(name=u'Rudolf')

# 名前を変更
# charsetを明示的に指定していない場合、my.cnf, my.iniの
# 設定によっては、UnicodeEncodeErrorとなると思います
c.name = u'ルドルフ'

# ここでコミット
db.flush()


 SqlSoupの返すMappedObjectは、__str__, __repr__の実装の中で、locale.getdefaultencoding()から得たエンコーディングで、ユニコード文字列をエンコードしようとするので、str(obj), unicode(obj), repr(obj)の出力は期待した通りに動作しません。(ここは、SQLAlchemyの内部文字セットに合わせてほしいところです。)
 しかし、それ以外は、挿入、更新、削除、特に問題なくユニコードを扱えるように見えます。

>>> cat = db.cat.get(2)
>>> cat
# repr(cat)がうまく動かない

>>> cat.name
u'\u30a4\u30c3\u30d1\u30a4\u30a2\u30c3\u30c6\u30ca'
>>> print cat.name
イッパイアッテナ
# 内部プロパティは正しくユニコードとして扱えている