среда, 23 апреля 2008 г.

z3c tutorial, часть третья

3 Создание форм при помощи z3c.form

По логике, следующим шагом для ZContact была бы возможность добавлять контакты, через форму, которая спрашивала бы у нас имя и фамилию нового контакта. Мы можем генерировать такую форму с помощью schema (IContact), определенную нами в interfaces.py с использованием пакета z3c.form

3.1 Добавление z3c.form и z3c.formui в качестве зависимостей

Однако, прежде чем мы сможем использовать z3c.form, мы должны добавить ее в качестве зависимости к нашему приложению.

Чтобы добавить z3c.form как зависимость, откройте setup.py в корневой директории вашего приложения и добавьте 'z3.form' параметру install_requires (где-то возле 25 линии :). В это время вам следует также добавить z3c.formui и z3c.layer в качестве зависимостей, которые необходимы для отрисовки в клевой верстке.

Следующее, что вы захотите, это включить zcml-конфигурацию для z3c.form и z3c.formui в файл configure.zcml, расположенный в zcontact/src/configure.zcml. z3c.form makes use (делает применение?) другие многочисленные компоненты z3c.*, которые определяют новые zcml-директивы.

Для использования этих директив вы должны включить метафайлы в начало файла сonfigure.zcml перед всеми другими инклюдами (include).


Следующее, что вам нужно, это включить реально (?) пакеты z3c.form и z3c.formui в конец конфигурационного файла configure.zcml.


Теперь все, что мы должны сделать, это перезапустить процесс сборки, чтобы загрузились и стали доступны новые яйца-капсулы для z3c.form и z3c.formui.

$ ./bin/buildout -N
Опция -N указана для того, чтобы не загружались уже скачанные яйца (настоятельно рекомендуется).

3.2 Настройка вашего приложения для работы с z3c.form

К сожалению, мы еще не совсем готовы к использованию z3c.form. Из-за того, что пакеты z3c созданы с неким другим шаблоном для создания приложений, необходимы ее кое-какие шаги, чтобы заставить их работать. Одним из таких шагов будет установка своего собственного слоя и скина. К несчастью, многие zcml-директивы не помогут вам объяснить, для какого слоя вам необходимо зарегистрировать страницу или вид. Если вы не укажете слой, он регистрируется со слоем по-умолчанию. Это отключит все те пакеты, которые зарегистрировали все свои собственные виды со слоем по-умолчанию, делая этот слой сильно загрязненным всяким ненужным нам барахлом. Избежать этого непотребства, пакеты z3c.* регистрируют все свои виды по запросу на слое, специфичному (?) пакету. Чтобы правильно использовать z3c.* пакет, вы должны расширить эти слои со своим собственным. Сейчас мы создадим слой, который позволит нам использовать виды из пакета z3c.form.

3.2.1 Создание слоя

Создайте новый файл src/zcontact/layer.py и добавьте в него следующий код:

1
2
3
4
5
from z3c.form.interfaces import IFormLayer
from z3c.layer.pagelet import IPageletBrowserLayer

class IZContactBrowserLayer(IFormLayer, IPageletBrowserLayer):
"""ZContact browser layer with form support."""

IFormLayer имеет виды для всех виджетов, используемых в генерируемых формах, а IPageletBrowserLayer предоставляет различные полезные виды утилит, таких как error.

3.2.2 Создание скина

Чтобы получить доступ к этому слою из браузера, мы должны создать скин. Итак, создадим еще один файл src/zcontact/skin.py и добавьте туда следующий код:

1
2
3
4
5
6
7
import z3c.formui.interfaces

from zcontact import layer

class IZContactBrowserSkin(z3c.formui.interfaces.IDivFormLayer,
layer.IZContactBrowserLayer):
"""The ZContact browser skin using the div-based layout."""

Заметьте, что наш скин наследуется от IDivFormLayer, определенный в пакете z3c.formui. Когда форма отрендерится, поля возникнут в тегах, а не в таблице. Существует еще слой для верстки, основанной на таблицах. Благодаря компонентной архитектуре, также возможно написать свой собственный слой верстки форм, но мы не будем здесь этого делать. :)
Сейчас мы должны зарегистрировать скин в zcml с новым файлом src/zcontact/skin.zcml. Мы сделаем доступным наш скин через http://localhost:8080/++skin++ZContact/. В файле будет следующее:


Не забудьте включить этот новый zcml-файл в zcontact/configure.zcml линией package="zcontact" file="skin.zcml" />.

3.3 Создание формы добавления

Мы начнем создание нового модуля zcontact.browser, добавив директорию browser в src/zcontact с пустым файлом __init__.py. Теперь мы можем создать и открыть новый файл zcontact/browser/contact.py, где мы определим все формы.
Начнем с добавления следующего кода в файл browser/contact.py:

1
2
3
4
5
6
7
8
9
from z3c.form import form, field

from zcontact import interfaces


class ContactAddForm(form.AddForm):
"""A simple add form for contacts."""

fields = field.Fields(interfaces.IContact)
Класс form.AddForm, от которого мы сейчас унаследовались, определяет для создания и добавления наших новых объектов несколько методов, которые мы переопределим позднее и на форме добавления можно найти несколько кнопок.
Затем мы добавим страницу, которая использует этот класс для отображения ормы. Откройте zcontact/browser/configure.zcml и добавьте следующее:

 1
2
3
4
5
6
7
8
9
10
11
 xmlns="http://namespaces.zope.org/browser">


name="addContact.html"
for="zope.app.folder.interfaces.IFolder"
permission="zope.Public"
layer="zcontact.layer.IZContactBrowserLayer"
class=".contact.ContactAddForm"
/>

Мы зарегистрировали страницу под именем addContact.html для интерфейса IFolder. Поскольку корневая папка каждого нового экземпляра zope реализует IFolder, мы должны иметь возможность (сможем) получить доступ к этой странице по адресу http://localhost:8080/++skin++ZContact/@@addContact.html. Не забудьте включить пакет browser в файл zcontact/configure.zcml, добавив в самый конец строчку package=".browser" />.
Теперь мы готовы к действию, итак, перезапустим сервер с помощью команды ./bin/paster serve deploy.ini (или debug.ini, если пожелаете) и зайдем по адресу http://localhost:8080/++skin++ZContact/@@addContact.html. Вы должны увидеть очень простую форму по типу:



3.4 Доводим до конца форму добавления

Если вы действительно попробовалииспользовать форму добавления и нажимали на кнопку, вы должны были получить ошибку NotImplemented. Если вы выбирали запуск paster с конфигурацией debug.ini, а не deploy.ini, вероятно вы получали клевый (прикольный) экран по типу следующего:



Из этово экрана вы можете раскрыть любую строчку в трассировке и вставить туда код python, чтобы отладить проблему. Я был очень удивлен (и я тоже, - прим. переводчика), когда впервые увидел это.
Чтобы исправить ошибку, мы должны реализовать три метода для класса ContactAddForm: create, add и nextURL. Я решил реализовать то по-быстрому, как показано ниже, хотя, конечно, никто не мешает вам сделать это по-своему.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from z3c.form import form, field

from zcontact import interfaces
from zcontact.contact import Contact


class ContactAddForm(form.AddForm):
"""A simple add form for contacts."""

fields = field.Fields(interfaces.IContact)

def create(self, data):
contact = Contact()
form.applyChanges(self, contact, data)
return contact

def add(self, contact):
self._name = "%s-%s" % (contact.lastName.lower(), contact.firstName.lower())
self.context[self._name] = contact

def nextURL(self):
return '/'

В методе create мы использовали функцию form.applyChanges для установки значений атрибутов нового контакта firstName и lastName. Данные, передаваемые методу create соотносятся между именами полей и введенными данными, уже приведенными к правильным типам python. Например, мы должны получить contact.firstName = data['firstName']. Я жестко установил методу nextURL возвращать путь, который отправлял бы нас назад к скину по-умолчанию Rotterdam, где вы сможете увидеть заново созданный контакт в виде содержимого. Мы сделали это, так как еще не написали свой собственные контентные виды для нашего слоя/скина и должны возвращаться в Rotterdam.

3.5 Формы отображения и редактирования

Формы отображения и редактирования еще проще, чем формы добавления, тк как вам не нужно реализовывать дополнительные методы. Давайте начнем с создания такой формы отображения.

3.5.1 Создание формы отображения

Для создания формы отображения нам понадобится новый класс, который бы наследовался от form.Form. Чтобы виджет отображался как обычный текст, а не форма ввода, мы должны установить для формы режим DISPLAY_MODE, это обычная константа, которую нужно импортировать из z3c.form.interfaces, (добавив строку импорта from z3c.form.interfaces import DISPLAY_MODE - прим. переводчика). Откройте файл zcontact/browser/contact.py и добавьте следующий код:

1
2
3
4
5
class ContactDisplayForm(form.Form):
"""A simple display form for contacts."""

fields = field.Fields(interfaces.IContact)
mode = DISPLAY_MODE

и не забудьте зарегистрировать новую форму в configure.zcml (не который корневой, а который в папке browser - прим. переводчика) с помощью следующего:

1
2
3
4
5
6
7

name="index.html"
for="..interfaces.IContact"
permission="zope.Public"
layer="zcontact.layer.IZContactBrowserLayer"
class=".contact.ContactDisplayForm"
/>

Сейчас, когда у нас есть форма отображения, мы можем изменить nextURL класса ContactAddForm, чтобы он указывал на вновь созданный контакт. Теперь он будет выглядеть следующим образом:

1
2
def nextURL(self):
return absoluteURL(self.context[self._name], self.request)


Не забудьте включить линию from zope.traversing.browser.absoluteurl import absoluteURL в начало файла! Если все получилось, вы можете рестартовать сервер и добавить новый контакт по адресу http://localhost:8080/++skin++ZContact/@@addContact.html, чтобы открыть форму отображения. Она будет выглядеть примерно так:





3.5.2 Добавление кнопок в форму

А теперь давайте-ка добавим две кнопочки, одну для редактирования, другую для удаления контакта. Начнем с добавления строчки from z3c.form import button в начало файла contact.py.

Когда пользователь нажимает кнопку, данные формы отправляются по адресу url, указанному в атрибуте action формы. По-умолчанию, action установлено в url самой формы, так что, когда нажимаем кнопку, порма просто перезагружается. Когда форма отрабатывается, проверяется, какая кнопка была нажата и вызываются соответствующие обработчики (handlers), которые определены как методы в классе ContactDisplayForm. Кроме того, при помощи z3c.form мы можем определить кнопку и обработчик, используя декоратор. Для кнопки удаления мы заходим удалить контакт и отправить пользователя назад на форму добавления. Добавим следующий код в класс ContactDisplayForm:

1
2
3
4
5
6
7
@button.buttonAndHandler(u'Delete', name='delete')
def handleDelete(self, action):
name = getName(self.context)
parent = getParent(self.context)
del parent[name]
nextURL = absoluteURL(parent, self.request)+'/@@addContact.html'
self.request.response.redirect(nextURL)

Убедитесь, что вы добавили следующие строчки импорта в начало файла:

1
2
3
4
from z3c.form import form, field, button
from z3c.form.interfaces import DISPLAY_MODE
from zope.traversing.browser.absoluteurl import absoluteURL
from zope.traversing.api import getParent, getName
(А у меня еще была строчка from zcontact.contact import Contact, может она не нужна?

Действительно, если ее упустить, то в дебаг-мод можно увидеть трейссинг следующего вида:


А еще конечно же не забываем про from zcontact import interfaces. Таким образом, у нас будет наверху следующий импорт:

from zope.traversing.browser.absoluteurl import absoluteURL
from z3c.form import form, field, button
from z3c.form.interfaces import DISPLAY_MODE
from zcontact import interfaces
from zcontact.contact import Contact
from zope.traversing.api import getParent, getName

Так что смотрим внимательно за тем, что делаем! - прим. переводчика)

Теперь добавим кнопку редактирования. У нас еще нет пока формы редактирования, так что кнопка редактирования будет вести нас на несуществующую страницу. Пока просто забьем место этим. Добавьте следующее в класс ContactDisplayForm:

1
2
3
4
@button.buttonAndHandler(u'Edit', name="edit")
def handleEdit(self, action):
nextURL = absoluteURL(self.context, self.request) + '/@@editContact.html'
self.request.response.redirect(nextURL)

Теперь вы можете рестартовать сервер и попробовать кнопки (Зайдите по адресу http://localhost:8080/++skin++ZContact/@@addContact.html, добавьте контакт и полюбуйтесь - прим. переводчика). Ваша форма будет похожа на следующую:




3.5.3 Создание форм для редактирования

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

1
2
3
4
class ContactEditForm(form.EditForm):
"""A simple edit form for contacts."""

fields = field.Fields(interfaces.IContact)
и всего-то еще необходимую zcml-конфигурацию:

1
2
3
4
5
6
7

name="editContact.html"
for="..interfaces.IContact"
permission="zope.Public"
layer="zcontact.layer.IZContactBrowserLayer"
class=".contact.ContactEditForm"
/>
Теперь возьмем и попробуем нашу форму для редактирования, нажав кнопку Edit с формы отображения (то есть сначала, конечно, нужно зайти по адресу http://localhost:8080/++skin++ZContact/@@addContact.html и добавить контакт, а потом с формы отображения нажать на Edit - прим. переводчика). Вы можете заметить, что мы уже имеем кнопку Apply для внесения изменений. После редактирования вам представят статусное сообщение об успехе или провале. С этого момента, мы вернемся к форме добавления и добавим кнопку 'Done' в класс ContactEditForm с помощью кода:

1
2
3
@button.buttonAndHandler(u'Done', name='done')
def handleDone(self, action):
self.request.response.redirect(absoluteURL(self.context, self.request))
Но постойте! Как только мы создали свою собственную кнопку, мы переопределили кнопки, декларируемые в классе form.EditForm. Чтобы избежать этого, мы должны расширить (extend) класс form.EditForm, поместив form.extends (form.EditForm) после декларации класса ContactEditForm. Теперь вы должны иметь форму для редактирования по типу следующей:




3.6 Завершение приложения заглавной страницей

Прежде чем мы ринемся в скиннинг, давайте сделаем еще одну страницу для целостности нашего приложения. Следующая страница будет заглавной страницей нашего приложения и будет предоставлять ссылку на форму добавления и ссылки на каждый существующий контакт. Это все можно сделать в простом шаблоне в zcontact/browser/frontpage.pt


Чтобы это появилось в главной странице нашего приложения, нам нужно зарегистрировать страницу в zcml для интерфейса IRootFolder. Добавим следующее в файл zcontact/browser/configure.zcml:


Перезагрузите ваш сервер и проверьте http://localhost:8080/++skin++ZContact/ и вы должны увидеть что-то похожее.



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

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

Анонимный комментирует...

Реально помогло