2008-01-11

Использования сложных полей ввода

Andrey Orlov  2008-01-11 20:17

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

Использование полей для ввода объектов:

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

Все достаточно просто и понятно, пока речь идет об "обычных" типах значений: строках или целых. Но что делать, если значение - это кортеж, множество или даже другой компонент? Оказывается, для каждого такого случая есть свои готовые поля.

Ввод кортежей:

Для ввода кортежей испольуется поле tuple. У такого поля есть специальный аргумент - value_type, значением которого является также поле. То, какое именно поле, вообще говоря, ограничено, но скорее ошибками реализации, а не злым умыслом. Итак пример:

            keyword = Tuple(
                name=u"Ключевые слова",
                description=u"Ключевые слова, используемые как теги",
                value_type = TextLine( max_length = 20),
            )

Подробности этого лучше посмотреть в ++apidoc++, на интерфейс ITuple (про пользование APIDOC можно почитать в статье "забавные места в скине Zope3".

Ввод значений выбором из списка:

Часто нужно ввести значение, выбрав один вариант из списка возможных. Это реализуется при помощи словарей и поля Choice:

            doctype = Choice(
                name=u"Тип документа",
                description=u"На сайте может существовать\
                     много типов документов, выберете \
                      пожалуйста один из них",
                vocabulary="DocumentTypes"
                )                      

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

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

            doctype = Set(
                name=u"Тип документа",
                description=u"На сайте может существовать\
                     много типов документов, выберете \
                      пожалуйста один из них",
                vocabulary="DocumentTypes"
                )                      

Как тип документа получается множественным - вопрос отдельный, хотя, в проектируемой нами системе такая возможность предусмотрена :), возможно, к концу курса вы сможете спроектировать такую.

Ввод компонент в качестве значений:

Как вы помните, компонент - это то, что имеет интерфейс. Например, интерфейс редактирования. Очевидно, что в нем много полей и все эти поля нужно отобразить на странице формы. С отображением все просто: например, обводим поля в рамку, сверху пишем название поля, а внутри - список полей, каждое со своим названием. Немного сложнее с написанием кода для этого. Чтобы его продемонстрировать, потребуется целый продукт bookmarknote. Продукт предоставляет компоненты, каждый из которых хранит кортеж компонент, предоставляющих интерфейс из ссылки и ее название, и плюс один точно такой же компонент, но вне кортежа. Непонятно? Давайте посмотрим на примере. Точные объяснения того, что где должно лежать в продукте под Zope3 будут даны в статье Скелет контент класса в Zope.txt, а пока ограничимся ссылкой на пример и на цитирование кода. Итак, для выполнения этой задачи нужно создать компонент, в котором будет хранится ссылка. Начнем с интерфейса:

            from zope.interface import Interface
            from zope.schema import TextLine, URI

            class IUrlText(Interface):

                url = URI(title=u'URI')

                title = TextLine(title=u'Title')

Теперь напишем компонент:

            from zope.interface import implements
            from interfaces import IUrlText

            class UrlText(Object) :
                implements(IUrlText)

                url = ""

                text = ""

Теперь опишем интерфейс компонента, содержащего компоненты UrlText в качестве атрибута и кортежа:

            from zope.interface import Interface
            from zope.schema import Tuple, Object
            from interfaces import IUrlText

                mainurltext = Object(
                    title=u'Main URL',
                    schema=IUrlText)

                urltext = Tuple(
                    title=u'urltext',
                    value_type=Object(
                    title=u'Main URL',
                    schema=IUrlText))

Напишем сам компонент:

            from zope.interface import implements
            from persistent import Persistent
            from zope.app.container.interfaces import IContained
            from zope.app.container.contained import Contained                
            from interfaces import IBookmarkNote

            class BookmarkNote(Contained,Persistent):

                implements(IBookmarkNote,IContained)

                mainurltext = None

                urltext = None

Про Persistent можно прочитать позже в статье ZODB and Persistent Objects.txt, а пока перейдем к формам редактирования, которые, как известно, должны генерироваться "сами собой". Попробуем декларировать форму добавления:

            <addform
                label="Add BookMark"
                name="AddBookMark.html"
                content_factory="..bookmarknote.BookmarkNote"
                permission="zope.ManageContent"
                schema="..interfaces.IBookmarkNote"
                >

Если попытаться ей воспользоваться, то выяснится, что она не работает: оказвается, поле object готового виджета не имеет. Это совсем не странно, но работа, как оказалось, потребовала создания виджета. К счастью, это типовой и не совсем полноценный виджет: он не будет регистрироваться (про создание полноценных виджетов можно прочитать в Программирование полей и виджетов.txt). Итак, типовой случай виджета для объекта с известным интерфейсом создается фабрикой CustomWidgetFactory:

            from zope.app.form import CustomWidgetFactory
            from zope.app.form.browser import ObjectWidget
            from zope.app.form.browser import TupleSequenceWidget
            from bookmarknote.urltext import UrlText

            UrlTextWidget = CustomWidgetFactory(
                    ObjectWidget,
                    UrlText)

            UrlTextTupleWidget = CustomWidgetFactory(
                TupleSequenceWidget,
                subwidget=CustomWidgetFactory(
                    ObjectWidget,
                    UrlText))

Здесь фигурируют два виджета: UrlTextWidget и UrlTextTupleWidget, второй приходится создавать потому, что было решено не регистрировать виджет UrlTextWidget, а значит стандартный widget для кортежа не сможет найти виджет UrlTextWidget для своего содержимого.

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

            <addform
                label="Add BookMark"
                name="AddBookMark.html"
                content_factory="..bookmarknote.BookmarkNote"
                permission="zope.ManageContent"
                schema="..interfaces.IBookmarkNote"
                >
              <widget
                  field="mainurltext"
                  class=".widgets.UrlTextWidget"
                  />
              <widget
                  field="urltext"
                  class=".widgets.UrlTextTupleWidget"
                  />
             </addform>

Попытка вызова показывает что все ОК :), тепер регистрируем форму в меню:

            <addMenuItem
                class="..bookmarknote.BookmarkNote"
                title="BookmarkNote"
                description="BookmarkNoote"
                permission="zope.ManageContent"
                view="AddBookMark.html"
            />

И, аналогично явно указав виджеты, регистрируем форму редактирования:

            <editform
                schema="..interfaces.IBookmarkNote"
                for="*"
                label="Edit"
                name="edit.html"
                permission="zope.ManageContent"
                menu="zmi_views" title="Edit"
                >
              <widget
                  field="mainurltext"
                  class=".widgets.UrlTextWidget"
                  />
              <widget
                  field="urltext"
                  class=".widgets.UrlTextTupleWidget"
                  />
            </editform>

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

Заключение:

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

Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml