Как использовать в словаре русские строки
2008-10-08 13:01Словарь в Zope3 используется, в основном, для создания интерфейсов с полями, допускающих выбор из нескольких возможных значений (такие поля отображаются в формах как выпадающие списки). Существует готовая реализация словаря SimpleVocabulary, позволяющая легко создавать словари просто перечислив их содержимое. Однако из-за ряда неточностей, как в реализации полей схем, так и в реализации самого SimpleVocabulary возникают трудности при использовании его со строками unicode (или utf-8). Эта статья рассказывает о причинах возникновения и способе решения проблемы.
Как использовать SimpleVocabulary с строками в unicode
Подробно работа со словарями описана в статье Руководство по использованию словарей. Там же приведено много примеров использования класса SimpleVocabulary.
Класс SimpleVocabulary это практически готовый к использованию словарь, который сильно облегчает программирование под Zope3. К сожалению, существует ряд неточностей в реализации полей интерфейсов, использующих словари, виджетов и самого словаря, из-за которых простая конструкция:
from zope.schema.vocabulary import SimpleVocabulary vocabulary = SimpleVocabulary.fromValues([u"раз",u"два"])
оказывается неработоспособной в случае использования строк unicode: русские слова не могут быть использованы в формах с элементами выбора, т.к. выбор любого русского слова считается ошибкой.
Почему
Чтобы объяснить причины этого явления, напомним, что словарь состоит из термов, каждый из которых включает в себя токен, значение и заголовок элемента словаря. Токен - это то, что идентифицирует терм. При использовании конструкции, указанной выше, терм создается при помощи SimpleTerm, который создает токен из значения приведением его к строке:
term.token = str(value)
Это вполне срабатывает даже при наличии национальных символов, при условии что вы установили utf-8 кодировкой по умолчанию для самого языка питон.
Однако, при последующей работе, при сохранении форм не происходит преобразования в строку значения, полученного из формы. Поэтому сравнение с токеном завершается ложным результатом (сравнивается одна и та же строка в unicode и в utf-8): иными словами, форма не находит в словаре введенное значение.
Как бороться
Внимательное изучение кодов показало, что адекватный метод решение проблемы - явный вызов SimpleTerm и самостоятельное преобразование к уникальной форме, не содержащей символов, кроме ascii:
from zope.schema.vocabulary import SimpleVocabulary,SimpleTerm
vocabulary = SimpleVocabulary(
[ SimpleTerm(x,token="".join(
["%04x" % ord(y) for y in list(unicode(x))]),
title=x
) for x in [u"Раз",u"Два"] ]
)
Обратите внимание, что в этом случае пришлось указать еще и title: иначе поле формы будет выведено значение токена. Такая конструкция сразу же начинает работать, хотя с прикладной точки зрения почти ничего не изменилось.
Заключение
Для того, чтобы облегчить жизнь пользователям Zope3, в библиотеку ng.lib-0.0.6 включена исправленная (несколько иным образом) версия SimpleVocabulary.



