Использования сложных полей ввода
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>
Ну, получилось сложновато для первого раза, но это пример нетипичный, и обратите внимание - это еще не пример программирования, это просто материал, данный с некотором упреждением, чтобы рассказать про использование схем почти все.
Заключение:
На примерах этой статьи можно убедится, как легко и просто создаются веб-интерфейсы для достаточно сложных компонентов. Очевидно, это достигается благодаря возможности легкого повторного использованя тысяч компонент,



