Personal tools
You are here: Home Статьи Основы использования словарей в Zope3
Document Actions

Основы использования словарей в Zope3

by Анатолий Бубенков last modified 2007-09-07 18:23
Contributors: Андрей Орлов, Егор Шершнев

Словарь - это объект специального вида, используемый в zope, в основном, в полях схем, как источник пар ключ-значения в полях вида Choice, Select и аналогичных. При этом словарь может использоваться самостоятельно (т.е. определятся где-то в коде и использоватся там же, без выноса на уровень компонентной архитектуры Zope3), так и в виде специальной утилиты, регистрируемой в реестре и доступной для использования на компонентном уровне.

Определение и состав:

Существует классическое определение словаря:

Словарь
это множество уникальных ключей, каждому из которых ставится в соответствие значение.

При работе со словарём, по любому данному ключу можно всегда получить связанное с этим ключом значение, либо убедиться, что данного ключа в словаре нет.

Поскольку использование и способ соотнесения ключа и его значения могут различатся (ключ и значение могу совпадать, могут быть различных типов) в среде zope3, для удобства разработчиков, используется другое определение словаря:

Словарь
это объект, представляющий собой коллекцию уникальных термов;
Терм
некоторая константа или переменная.

Понятие уникальности терма сильно зависит от типа терма, которых в Zope3 может быть три:

Простой терм
терм, имеющий только значение (value), которое должно быть уникальным в пределах словаря. В действительности, данный терм имеет и ключи, но эти ключи в точности соответствуют значениям, приведённым к строковому типу.

Пример простого терма: {1:1, 2:2, 3:3}

Терм с лексемой
терм, который, помимо значения, содержит еще и некоторую лексему (token), или, как её ещё называют, токен. Под токеном понимается строка без управляющих символов, определяющая идентификатор данного терма. В пределах словаря токен должен быть уникальным, так как наличие хотя бы двух одинаковых токенов нарушит требование взаимной однозначности соответствия "токен-значение".

Пример терма с токеном: {1:red, 2:green, 3:blue}

Терм с названием
терм, который помимо токена и значения содержит ещё и некоторое название, которое представляет собой удобную для прочтения человеком строку, которая не обязательно должна быть уникальна в пределах словаря, и которая характеризует собой пару токен-значение. Данная строка очень полезна в том случае, если словарь хранит группы значений, которые можно объединить по некоторому признаку. Для предыдущего примера этим признаком может быть, например строка "colors".

Данное определение не противоречит общеринятому, а лишь адаптирует его к использованию в рамках Zope3.

Интерфейсы, предоставляемые словарями в Zope3 :

Использование словарей на уровне компонентной модели требует, чтобы словарь предоставлял реализацию специальных интерфейсов. В Zope3 наиболее часто используются следующие стандартные интерфейсы:

ITerm
объект, представляющий собой простое значение (value) в словаре. Данный интерфейс имеет единственный атрибут value.
ITokenizedTerm
наследник интерфейса ITerm, это интерфейс терма словаря, содержащего атрибуты value (унаследованный от ITerm) и token (лексема, представляющая собой 7-битную строку, не содержащую управляющих символов).
ITitledTokenizedTerm
наследник интерфейса ITokenizedTerm, интерфейс терма словаря, состоящего из токена, значения (унаследованы) и лексемы (title).
ISource
интерфейс набора значений. Данные значения используются в качестве элементов, из которых следует выбрать нужные, в полях типа choise. Набор значений может быть большим, и даже бесконечным. Данный интерфейс имеет метод __contains__(value), который позволяет проверять вхождение элемента с указанным значением (value) в словарь.
IBaseVocabulary
интерфейс словаря, наследник интерфейса ISource. Это наиболее общий и базовый интерфейс словаря, в котором описан единственный метод getTerm(value), позволяющий получить некоторый терм словаря по известному значению value. Если происходит попытка получить несуществующий терм, возникает LookupError.
IIterableVocabulary
итерируемый словарь, который поддерживает итерации по разрешённым значениям. Предположительно прекратит своё существование в Zope 3.3, но в настоящий момент является используемым.

Интерфейс декларирует 2 метода:

__iter__
позволяет получить значение итератора по терму;
__len__
возвращает количество термов словаря, либо значение sys.maxint;
IVocabulary
интерфейс итерируемого словаря. Является наследником интерфейсов IIterableVocabulary и IBaseVocabulary.
IVocabularyTokenized
интерфейс словаря, поддерживающего хранение элементов с лексемами (токенами). Метод getTermByToken позволяет получить терм по известной лексеме. Является наследником интерфейса IVocabulary.

Примеры использования словаря:

В качестве примера использования словаря на уровне компонентной модели приведём программный код, получающий интерфейс по его полному точечному имени. Воспользуемся для иллюстрации отладчиком Zope, запустите bin/debugzope и введите:

        from zope.schema.interfaces import IVocabularyFactory
        from zope.app.zapi import getUtility

        util = getUtility(IVocabularyFactory, name='Interfaces', context=root)

        # создаем словарь
        vocabulary = util(root)

        # получаем терм по лексеме - 
        # полному точечному имени интерфейса

        token = vocabulary.getTermByToken('zope.interface.Interface')

        # получаем и возвращаем описание интерфейса
        print token.token

        # получаем сам интерфейс
        print token.value

Здесь использовался стандартный словарь Interfaces, который был получен из стандартного реестра утилит. Реестр содержит множество полезных словарей, полный список которых можно увидеть примерно тут:

        http://localhost80/++apidoc++/Utility/%40%40menu.html            

Упомянутая в примере переменная root - это контекст вызова, в отладчике используется корневой объект ZODB, внутри вашего кода - обычно перзистент объект, в рамках которого вы вызываете данный код (self)

Работать со словарями ниже уровня компонентной модели несравненно проще, главное, чтобы было чем их заполнить:

        from zope.schema.vocabulary import SimpleVocabulary

        # Создадим словарь
        vocabulary = SimpleVocabulary.fromValues([1, 2, 3])

        # Распечатаем список значений
        print list(vocabulary)

        # Получим по ключу терм
        term= vocabulary.getTerm(1)

        # Распечатаем токен
        print term.token

        # Распечатаем значение
        print term.value

Как видим, это совсем обычная сущность, просто с некоторыми особенностями.

Использование словарей в полях схем :

В отличие от примеров предыдущего параграфа, в zope3 наиболее распространено использование словарей в полях схем. Для наглядности, приведём два примера подобного использования словарей.

Пример: Использование словаря с известными значениями

Один из наиболее простых способов - задание экземпляра словаря с заранее известными значениями. Примером подобного словаря может быть любой заведомо известный и неизменяемый набор данных, например набор цветов светофора.

Интерфейс с полем myChoice, которое является словарём из заданных значений, определяется следующим образом:

            from zope.schema.vocabulary import SimpleVocabulary
            from zope.interface import Interface
            from zope.schema import Choice

            class IMyInterface(Interface):

                myChoice = Choice(title=u'My Choice',
                            vocabulary = SimpleVocabluary.fromValues([1, 2, 3])
                           )

Здесь, значением свойства myChoice будет значение словаря, которое выберет пользователь (а не весь словарь сразу).

Пример: Использование словаря на уровне компонентной модели

Недостатком задания словаря в коде является невозможность (или затруденность) его многократного использования или декомпозиции кода на независимые компоненты. Естественное решение в рамках Zope3 - перейти на уровень компонент.

В этом случе использование словаря будет включать в себя три независимых действия:

  1. Выбрать (придумать) имя для утилиты с интерфейсом IVocabularyFactory (фабрика словаря);
  2. Создать класс (либо метод) фабрики словаря и зарегистрировать фабрику с выбранным именем;
  3. Присвоить выбранное имя атрибуту vocabulary поля, использующего словарь.

Еще раз отметим, в этом случае словарь и использующий его класс являются полностью независимыми и взаимозаменяемыми компонентами. А теперь код.

Создание словаря в файле myvocabulary.py:

            from zope.schema.vocabulary import SimpleVocabulary

            def myValuesVocabulary(context):
                return SimpleVocabulary.fromValues([1, 2, 3])

Регистрация созданного словаря в фале configure.zcml (для этого указывается интерфейс возвращаемого фабрикой объекта, собственно, фабрика и имя словаря):

            <configure
                xmlns="http://namespaces.zope.org/zope">

                <utility
                    provides="zope.schema.interfaces.IVocabularyFactory"
                    component=".myvocabulary.myValuesVocabulary"
                    name="My Values"
                    />

                </configure>

Использование созданного словаря в файле interfaces.py:

            from zope.interface import Interface
            from zope.schema import Choice

            class IMyInterface(Interface):

                myChoice = Choice(title=u'My Choice',
                            vocabulary = 'My Values'
                            )

Приведенному примеру стоит следовать, если вы хотите использовать словарь как независимую компоненту.

Примечание:

При создании словаря в фабрику будет передано значение объекта, в контексте которого создается словарь, что позволяет использовать контекстно-зависимые словари. При этом надо учесть, что если для форм редактирования контекстом является редактируемый объект, то для форм добавления - объект zope.app.container.interfaces.IAdding, контекстом которого, в свою очередь, является редактируемый объект.

Создание словарей:

Уже отмечалось, что в реестре утилит Zope3 зарегистрировано множество полезных словарей. Тем не менее, необходимость в создании собственного словаря возникает настолько часто, что существует несколько готовых инструментов для решения такой задачи. Рассмотрим три наиболее популярные из них.

Создание словаря из комплектующих :

В этом случае словарь объявляется непосредственно в исходном коде, причём к требуемому результату в этом случае можно прийти тремя различными путями, а именно:

  1. В классе терма реализовать интерфейс ITitledTokenizedTerm, либо воспользоваться готовым zope.schema.vocabulary.SimpleTerm (со свойствами value, token и title);
  2. В классе словаря реализовать интерфейс IVocabularyTokenized, либо воспользоваться готовым zope.schema.vocabulary.SimpleVocabulary с нужными свойствами и методами;
  3. Использовать класс словаря напрямую, либо реализовать фабрику словарей, зарегистрировав ее с определенным именем и интерфейсом (zope.schema.interfaces.IVocabularyFactory).

Создание словаря с использованием готовых решений:

Данный способ создания словаря сводится к созданию (или регистрации) объекта, с помощью уже существющих, специально предназначеных для этого zope3 классов. В этом случае задача сводится к выбору того, каким способом создать словарь будет наиболее удобно в данном конкретном случае. При этом для создания словаря рекомендуется пользоваться одним из следующих методов класса zope.schema.vocabulary.SimpleVocabulary:

fromItems
позволяет создать словарь из списка пар (лексема, значение). В качестве названия (title) в этом случае будут использованы лексемы;
fromValues
позволяет создать словарь из списка значений. В качестве названий и лексем будут использованы значения;
__init__
стандартный конструктор. Принимает список уже сформированных термов и на основе этого списка создает словарь.

Создание словаря с помощью SqlVocabulary:

SqlVocabulary - фабрика словарей, которые заполняются данными, полученными с помощью sql-запроса. Данный компонент является наследником zope.app.sqlscript.SQLScript. Запрос, результаты которого используются для создания словаря, должен возвращать ровно 2 колонки: значение (в данном случае оно равно лексеме) и название. Фабрику, выполяющую необходимые действия, необходимо регистрировать с нужным именем и интерфейсом zope.schema.interfaces.IVocabularyFactory.

Примечание:

Продукт SqlVocabulary планируется включить в sqltools

Создание словаря с помощью СontentClass2Resource:

СontentClass2Resource - расширяемое хранилище описаний контент-классов. По содержимому поддерживает набор зарегистрированных фабрик словарей по каждому атрибуту описания контент-класса contentclass2resource.interfaces.IContentClassResource. Имена зарегистрированных фабрик выбираются с учетом выбранного префикса, например rescon_call.

Примечание:

Продукт СontentClass2Resource планируется опубликовать: contentclass2resourse

Базовые классы для создания словарей и работы с их элементами:

В стандартной поставке zope3 реализовано значительное количество классов для работы со словарями. Основными из них являются следующие:

zope.schema.vocabulary.SimpleTerm
базовый терм, реализует zope.schema.interfaces.ITitledTokenizedTerm;
zope.schema.vocabulary.SimpleVocabulary
базовый словарь, реализует zope.schema.interfaces.IVocabularyTokenized;
zope.app.component.vocabulary.UtilityTerm
терм, представляющий собой утилиту, реализует zope.schema.interfaces.ITokenizedTerm;
zope.app.component.vocabulary.UtilityNameTerm
терм, представляющий собой имя утилиты, реализует zope.schema.interfaces.ITokenizedTerm;
zope.app.component.vocabulary.UtilityVocabulary
словарь, представляющий собой набор утилит, реализует zope.schema.interfaces.IVocabularyTokenized.

Как можно видеть, указанный перечень классов покрывает большую часть интерфейсов, о которых шла речь в предыдущих раделах. Использование указанных классов позволяет реализовать практически любую задачу, посвящённую словарям и работе с ними.

Решения на основе словарей:

Для разработчикое, которые в своей практической деятельности не сталкивались со словарями, этот тип данных может показаться достаточно экзотическим и малопригодным, однако, подобное впечателние является неправильным. В подтверждение, приведём примеры кода по формированию и использованию словарей, которые взяты из реальных задач программирования. Каждый пример, как того требует zope3, состоит из двух файлов: файла с кодом на языке Python и файла с zcml-директивами.

Формирование словаря индексов:

В данном примере термы словаря получаются в результате запроса к каталогу zope.app.catalog.interfaces.ICatalog, который позволяет индексировать объекты, расположенные в ZODB и должен быть уже знаком zope- и plone-разработчикам. Для получения объекта, соответствующего индексу, при формировании каждого отдельно взятого терма словаря, используется генератор уникальных идентификаторов (утилита, реализующая интерфейс IIntIds).

Содержимое файла newsvocabulary.py:

            from zope.app.zapi import getUtility
            from zope.app.catalog.interfaces import ICatalog
            from zope.app.intid.interfaces IIntIds
            from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary

            def newsVocabulary(context):
                catalog = getUtility(context = context, interface = ICatalog)
                ids = getUtility(context = context, interface = IIntIds)
                news = [(i, ids.getObject(i)) for i in catalog.apply(someIndex={published=True})]
                return SimpleVocabulary([SimpleTerm(title=i.title, value = i, token=id) for (id, i) in news])

Содержимое файла configure.zcml:

            <configure
              xmlns="http://namespaces.zope.org/zope">

               <utility
                  provides="zope.schema.interfaces.IVocabularyFactory"
                  component=".newsvocabulary.newsVocabulary"
                  name="PublishedNews"
                  />

               </configure>

Пример является достаточно наглядным и простым. В файле newsvocabulary.py происходит импортирование всех нужных классов и реализована фабрика newsVocabulary. Внутри фабрики происходит получение утилит с интерфейсами ICatalog (это, собственно, каталог) и IIntIds (генератор уникальных идентификаторов), после чего происходит выборка проиндексированых каталогом объектов в опубликованном состоянии и получение самих объектов. Последняя строка фабрики, собственно, формирует словарь из списка объектов и их индексов. Файл configure.zcml содержит код регистрации утилиты с указанным именем и предоставляемыми интерфейсами.

Выбор автора из существующего списка пользователей:

В данном примере описано создание словаря, содержащего информацию обо всех пользователях системы. Впоследствии данный словарь можно будет использовать в некоторой схеме для указания, например, автора того или иного документа из числа зарегистрированных пользователей сайта.

Содержимое файла authorsvocabulary.py:

            from zope.app.zapi import getUtility
            from zope.app.security.interfaces import IAuthentication
            from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary

            def authorsVocabulary(context):
                auth = getUtility(context = context, interface = IAuthentication)
                plugins = auth.getAuthenticatorPlugins()
                principals = []
                for i in plugins:
                    principals.append(
                        [i.principalInfo(k) for k in i.search({'search': ''}]
                        )
                return SimpleVocabulary(
                    [SimpleTerm(title=i.title, value = i, token=i.id) 
                    for i in principals]
                    )

Содержимое файла configure.zcml:

            <configure
              xmlns="http://namespaces.zope.org/zope">

               <utility
                  provides="zope.schema.interfaces.IVocabularyFactory"
                  component=".newsvocabulary.newsVocabulary"
                  name="Authors"
                  />

               </configure>

Как видно, единственным принципиальным отличием данного примера от предыдущего является более сложное формирование списка (principals), для его дальнейшего преобразования в словарь.

Заключение:

В настоящем документе были затронуты лишь основные моменты работы со словарями, что позволит при необходимости либо использовать имеющиеся словари, либо конструировать собственные. Приведённые примеры показывают, что работа со словарями является весьма простой и однообразной, а вся разница при работе с базовыми вариантами словарей сводится к разнице способов получения значений для словаря.

Очевидной проблемой при использовании словарей является отсутствие возможности динамического изменения его содержимого по запросу. Примером такой задачи может быть постоянное обновление данных об авторах в примере с выбором автора из существующего списка пользователей, которое не реализуемо стандартными средствами по работе со словарями.

Рекомендуемые источники дополнительной информации по словарям:

http://wiki.zope.org/zope3/VocabularyFields

http://wiki.zope.org/zope3/VocabularyRegistry

zope/schema/vocabulary.py


Powered by Plone CMS, the Open Source Content Management System