Проблема сопоставления mysql-python: как заставить тип данных Unicode? - PullRequest
2 голосов
/ 01 марта 2012

Для определенных целей мне пришлось изменить параметры сортировки полей с utf8_unicode_ci на utf8_bin в базе данных. Оказалось, что изменения приводят к изменениям типов данных, которые приходят в Python.

Вопрос в том, , как заставить 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, а это не то, что я хочу.

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Оказывается, проблема довольно неловкая. Короче говоря, большинство разновидностей и видов в типах строковых данных MySQL отображаются на один тип данных в интерфейсе MySQL с дополнительным флагом BINARY.

Таким образом, MySQL VARCHAR, VARBINARY и строковый литерал отображаются в один и тот же тип MySQLdb.constants.FIELD_TYPE.VAR_STRING в определениях типа столбца, но имеют дополнительный флаг MySQLdb.constants.FLAG.BINARY, когда тип является VARBINARY или строка сопоставлена с сопоставлением *_bin.

Несмотря на то, что есть тип MySQLdb.constants.FIELD_TYPE.VARCHAR, я не смог выяснить, когда он используется. Как я уже сказал, MySQL VARCHAR столбцы отображаются на FIELD_TYPE.VAR_STRING.

Решение становится довольно хрупким, если ваше приложение использует истинные двоичные строки (например, вы храните изображения и извлекаете их с тем же соединением, что и текст), поскольку оно предполагает декодирование всех двоичных строк в Unicode. Хотя, это работает.

Как официальный документы заявляет:

Поскольку MySQL возвращает все данные в виде строк и ожидает, что вы преобразуете их самостоятельно. Это было бы настоящей болью в заднице, но на самом деле, _mysql может сделать это для вас. (И MySQLdb делает это за вас.) Чтобы выполнить автоматическое преобразование типов, вам нужно создать словарь конвертера типов и передать его в connect () в качестве параметра ключевого слова conv .

На практике настоящей болью в заднице может быть процесс создания собственного словаря конвертеров. Но вы можете импортировать файл по умолчанию из MySQLdb.converters.conversions и пропатчить его, или даже пропатчить его на экземпляре Соединения. Хитрость заключается в том, чтобы удалить специальный конвертер для флага FLAG.BINARY и добавить декодер для всех случаев. Если вы явно указываете параметр charset для MySQLdb.connect, он вызывает параметр use_unicode=1, который добавляет декодер для вас, но вы можете сделать это самостоятельно:

>>> 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',)

Возможно, вам потребуется сделать такой же взлом для FIELD_TYPE.STRING, если потребуется.

Другим решением является передача явного use_unicode=0 в MySQLdb.connect и выполнение всех расшифровок в вашем коде, но я бы не стал.

Надеюсь, это может быть кому-то полезно.

1 голос
/ 02 марта 2012

Это большое изменение по сравнению с использованием Mysql-Python на низком уровне, но я думаю, что лучшая идея - использовать что-то вроде sqlalchemy вместо непосредственного использования db-api, тогда вы можете использовать например types.Unicode и знайте, что он делает то, что требуется для поддержки юникода для db-api

Прежде чем вы поймаете меня на том, что я не отвечаю на вопрос напрямую, рассмотрим следующее: mysql-python или MySQLdb - это только один из нескольких db-api для MySQL. Вероятно, MySQLdb продолжит поддерживаться новыми версиями, но есть обстоятельства (например, переход на Python 3x или хост, на который у вас нет возможности устанавливать бинарные модули), которые могут вынудить вас использовать что-то другое в будущем, например как oursql или myconnpy . Люди, которые делают sqlalchemy, приложили немало усилий для поддержки нескольких db-api, а в случае с mysql-python в прошлом даже обходили серьезные ошибки. При использовании sqlalchemy переключение на другой db-api будет таким же простым, как и изменение URL-адреса соединения, и это обеспечит обработку всего, что связано с приведением типов данных, как и следовало ожидать.

Тем не менее, чтобы использовать это, вам нужно будет определить свои таблицы в терминах схем sqlalchemy и использовать их API запросов, но вы получите за это немало.

...