2012-03-01 12 views
2

特定の目的のために、データベース内のフィールド照合をutf8_unicode_ciからutf8_binに変更する必要がありました。変更により、Pythonに来るデータ型が変更されることが判明しました。mysql-python照合の問題:Unicodeデータ型を強制する方法?

質問はmysql-pythonにUnicodeオブジェクトをPythonに返すように強制する方法です。ここで

は、問題を示すサンプル(明示的な文字セット力use_unicode = 1)である:私のデータベースで

>>> con = MySQLdb.connect(..., charset='utf8') 
>>> c = c.cursor() 
>>> c.execute('SELECT %s COLLATE utf8_bin', u'м') 
1L 
>>> c.fetchone() 
('\xd0\xbc',) 
>>> c.description 
(("'\xd0\xbc' COLLATE utf8_bin", 253, 2, 3, 3, 31, 0),) 


>>> c.execute('SELECT %s COLLATE utf8_unicode_ci', u'м') 
1L 
>>> c.fetchone() 
(u'\u043c',) 
>>> c.description 
(("'\xd0\xbc' COLLATE utf8_unicode_ci", 253, 2, 3, 3, 31, 0),) 

ものではありませんどのフィールドがタイプVARCHARのですが、変更後に、彼らはBINARYのように振る舞いますが欲しいです。

+0

'SET NAMES utf8'? – Robus

+0

@ Roob、 'charset = utf8'はまったく同じです – newtover

+1

私はコメントとして投稿したのは確かではありませんでした:)実際にはPython側の変数をデコードするのはmysqlよりも手間がかかりません。 – Robus

答えて

2

問題はかなり厄介です。要するに、MySQL string datatypesのほとんどの変数と種は、追加のBINARYフラグを持つMySQLのインタフェース内の単一のデータ型にマップされます。タイプがVARBINARY又は*_bin照合と照合文字列である場合

したがって、MySQLのVARCHARVARBINARY、カラム型定義に同じMySQLdb.constants.FIELD_TYPE.VAR_STRINGタイプに文字列リテラルマップが、追加のMySQLdb.constants.FLAG.BINARYフラグを有します。

MySQLdb.constants.FIELD_TYPE.VARCHARタイプがあるにもかかわらず、私はそれがいつ使用されるかを知ることができませんでした。私が言ったように、MySQL VARCHARの列はFIELD_TYPE.VAR_STRINGにマップされます。

アプリケーションですべてのバイナリ文字列をUnicodeにデコードすることを前提としているため、アプリケーションで真のバイナリ文字列を使用すると(たとえば、画像を保存してテキストと同じ接続でフェッチするなど)しかし、それは動作します。公式docs状態として

MySQLは文字列として、すべてのデータを返し、あなたはそれを自分で変換するために想定しているため。これはお尻の本当の痛みですが、実際には、_mysqlはあなたのためにこれを行うことができます。 (そして、MySQLdbはこれをやります。)自動型変換を行うには、型変換辞書を作成し、これをconvキーワードパラメータとしてconnect()に渡す必要があります。

実際には、お尻の本当の苦痛は、あなた自身のコンバーター辞書を構築するプロセスかもしれません。しかし、MySQLdb.converters.conversionsからデフォルトのものをインポートしてパッチを適用することも、Connectionのインスタンスにパッチを適用することもできます。トリックは、FLAG.BINARYフラグの特別なコンバータを削除し、すべてのケースに対してデコーダを追加することです。あなたが明示的にMySQLdb.connectためcharsetパラメータを指定した場合、それはあなたのためのデコーダを追加use_unicode=1パラメータを、強制的に、しかし、あなたはそれを自分で行うことができます:あなたはおそらく必要な場合FIELD_TYPE.STRINGに同じハックを作るために必要がある場合があります

>>> con = MySQLdb.connect(**params) 
>>> con.converter[FIELD_TYPE.VAR_STRING] 
[(128, <type 'str'>), (None, <function string_decoder at 0x01FFA130>)] 
>>> con.converter[FIELD_TYPE.VAR_STRING] = [(None, con.string_decoder)] 
>>> c = con.cursor() 
>>> c.execute("SELECT %s COLLATE utf8_bin", u'м') 
1L 
>>> c.fetchone() 
(u'\u043c',) 

もう1つの解決策は、をMySQLdb.connectに明示的に渡し、コード内ですべての解読を行うことですが、そうではありません。

希望すると、これは誰かにとって役に立ちます。

1

低レベルでMysql-Pythonを使用すると、かなりの変更がありますが、db-apiを直接使用するのではなく、sqlalchemyのようなものを使用することをお勧めします。 types.Unicodeとそれがdb-apiのユニコードサポートに必要なことをしていることを知ってください

質問に直接答えることができないために私のところに飛び込む前に、これを考えてみましょう:mysql-python aka MySQLdbはいくつかのdb- MySQLの場合MySQLdbは新しいバージョンで引き続きサポートされる可能性がありますが、将来的に何か他のものを使用する可能性のある環境(例えばPython 3xへの移行や、バイナリモジュールをインストールする能力のないホストなど)がありますoursqlまたはmyconnpyとなります。 sqlalchemyを作成する人は、複数のdb-apiをサポートするために多くの努力を払っています。mysql-pythonの場合は、過去に深刻なバグを回避していました。 sqlalchemyを使用すると、別のdb-apiに変更するだけで接続URLを変更することができ、データ型変換のすべてが期待どおりに処理されるようになります。

これを利用するには、sqlalchemyのスキーマの観点からテーブルを定義し、そのクエリAPIを使用する必要がありますが、そのためには多大な労力が必要です。

関連する問題