2009-12-11

Pylons, SQLAlchemy, кодировка

Долгие мучения с вышеуказанной связкой побудили меня написать эту заметку.

Работа с SQLite не вызывала проблем, т.к. эта СУБД хранит данные в Unicode, и Pylons+mako по умолчанию с ним отлично работают. Просмотр данных в SQLite можно делать с помощью:
1) консольной утилиты (sudo apt-get install sqlite3)
2) SQLite Database browser (sudo apt-get install sqlitebrowser)
3) плагином для Firefox (http://code.google.com/p/sqlite-manager/)
Все они нормально отображают данные, без крякозябрей. На web-страницы шаблонизатор mako также выводит все корректно.

А вот с MySQL дело несколько сложнее, т.к. он может отдавать данные в разных кодировках. Чтобы получить данные в нужной, а не в той, что стоит в настройках сервера по умолчанию (они располагаются в /etc/mysql/my.cnf), нужно дать команду "SET NAMES кодировка".
Моя история была такова, что потребовалось данные из одной БД, которую использовала CMS Joomla, перетащить в другую, чтобы работать с ней из Pylons. Как я понял, Joomla при работе с БД использовала "SET NAMES latin1", что для англоязычных товарищей - нормально и привычно уже много лет. И все бы ничего, да есть засада.
Программы для работы с MySQL, которые я использую в основном, это:
1) MySQL Query Browser (sudo apt-get install mysql-query-browser)
2) phpMyAdmin на сервере
Эти приложения по умолчанию предпочитают кодировку соединения UTF8. Поэтому, что в одном, что в другом кириллица была видна как "ПеÑ". Есть приложения, которые справляются с этой проблемой, но в phpMyAdmin на сервере этого не исправишь.
Так что-же делать? Перевести данные из одной кодировки в другую. Мне пришлось написать скрипт, который создает два соединения с MySQL, причем в первом устанавливаю "SET NAMES latin1" (для донорской базы), во втором "SET NAMES utf8" (для новой). Остается пройтись по записям, считывая их в latin1 и записывая в utf8.
После такой обработки вышеприведенные программы отображают кириллицу как полагается. Остается только настроить Pylons и SQLAlchemy.
Итак, для корректного отображения данных нужно дать серверу MySQL команду "SET NAMES utf8". Где же это сделать? Я нашел три варианта. Предположим, проект называется app.
1) Файл /app/config/environment.py, метод load_environment
from sqlalchemy import engine_from_config
from app.model import init_model
def load_environment(global_conf, app_conf):
    engine = engine_from_config(config, 'sqlalchemy.')
    engine.execute("SET NAMES utf8")
    init_model(engine)
2) Файл /app/model/__init__.py, метод init_model
def init_model(engine):
    engine.execute("SET NAMES utf8")
В обоих случаях, при старте проекта в консоли будет видно, что эти команды выполняются. И все вроде бы пошло гладко! Но это до поры. А именно, до поры создания нового соединения.
Как известно, SQLAlchemy использует так называемый pool, т.е. некий список соединений, который использует по мере занятости, циклически. Их колличество можно менять. Пока я этого не понял, неоднократно был удивляем странным поведением пилонов. Так вот, пока хватает одного соединения, все идет как задумано, но стоит перейти на второе - косяки, вперед! "SET NAMES" для них никто не задавал, поэтому кодировка будет "как получится", а именно знаками вопросов для кириллицы. Вот тут то и пригодится третий способ. Он задает команду при создании соединения, и все pool-ы будут передавать данные с нужной кодировкой. Почитать об этом больше можно тут.
3) Файл /development.ini
sqlalchemy.url = mysql://root:@localhost:3306/church?init_command=set%20names%20%22utf8%22
sqlalchemy.convert_unicode=true
sqlalchemy.encoding='utf8'

Теперь немного о причине таких махинаций. Только таким образом мне удалось добиться того, чтобы поиск русских слов в базе данных происходил без учета регистра. В SQLite он не работает, т.к. там Unicode, и выбирать не приходится. И только в MySQL, и с данными в utf8 это получилось. Кстати, со словами, состоящими из латинских символов, проблем нет. Если есть другие способы, буду рад добавить их.

1 комментарий:

Unknown комментирует...
Этот комментарий был удален администратором блога.