Personal tools
You are here: Home Статьи Использование модификатора "method:" в Zope3
Document Actions

Использование модификатора "method:" в Zope3

by cray last modified 2006-12-13 14:46

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

Использование модификатора "method:" в Zope3:

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

        zope.publisher.browser.BrowserRequest.processInputs

Подробному рассказу об этом место в другой статье, а здесь расскажем о модификаторе :method. Модификатор method широко использовался в Zope2, но в Zope3 используется достаточно редко, возможно, из-за низкой информированности разработчиков.

Попробуем восполнить этот пробел приведя пример, удобный для копирования: ответим на три вопроса: зачем, идея решения и пример.

Зачем? или постановка задачи :

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

Характерный пример такой формы - управление различными контейнерами: от папки до корзины покупок.

Идея решения :

Свяжем с каждой кнопкой submit свое собственное имя, например:

            <input type="submit" name="ww0" value="..."/>

            ...     ...     ...     ...     ...     ...

            <input type="submit" name="wwn" value="..."/>

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

Так пишут все - от perl:CGI до PHP, но разработчики Zope3 (точнее говоря, Zope2), вынесли проверку этого ключа в специальный диспетчер. Как это работает: если в запросе обнаруживается ключ с модификтором method (например ww0:method), то по окончании процедуры траверса, будет вызван не последний объект, найденный в траверсе, а метод ww0 этого объекта.

Причем, безразлично о каком объекте идет речь: контент-объекте или адаптере вида. Важно, что бы аттрибут ww0 был и его можно было вызвать.

Пример 1: Простой и неправильный :

Что бы протестировать вышеописанный диспетчер, напишем два zpt-метода: qq и ww.

Метод qq:

            <html>
            <body>
            <h1>QQ</H1>
            <form action="" tal:attributes="action context/@@absolute_url">

            <input type="submit" name="qq:method" value="QQ"/>
            <input type="submit" name="ww:method" value="WW"/>

            </form>
            </body>
            </html>        

Метод ww:

            <html>
            <body>
            <h1>WW</H1>
            <form action="" tal:attributes="action context/@@absolute_url">

            <input type="submit" name="qq:method" value="QQ"/>
            <input type="submit" name="ww:method" value="WW"/>

            </form>
            </body>
            </html>        

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

Раз все работает, то почему пример неверный? Потому, что в программировании под Zope3 принято разделять контент-объект и адаптер вида, причем вся логика взаимодействия с пользователем выносится именно в адаптер вида, сущетственная часть которого, зачастую, пишется на питоне.

Пример 2: Сложный и из реальной жизни:

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

Начнем с темлейта statistic.pt:

            <html metal:use-macro="views/standard_macros/view">
            <body metal:fill-slot="body">
            <h1>Cache Statistics</H1>
            <dl>
                <dt>Size:</dt><dd tal:content="context/__len__">0</dd>
                <tal:block tal:condition="options/count | nothing">
                    <dt>Regenerated:</dt>
                    <dd tal:content="options/count | nothing">0</dd>
                </tal:block>
                <tal:block tal:condition="options/count | nothing">
                    <dt>Elapsed:</dt>
                    <dd tal:content="options/elapsed | nothing">0</dd>
                </tal:block>
            </dl>

            <form action="" tal:attributes="action string: ${context/@@absolute_url}/">
              <input type="submit" name="regenerate_all:method" value="Regenerate All"/>
              <input type="submit" name="clean:method" value="Clean"/>
            </form>

            </body>
            </html>

В первой части темплейта выводится вышеупомянутая статистика, в т.ч. статистика выполняемых методов (обратите внимание на использование пространства имен options, который будут упомянут ниже). Вторая часть - это уже знакомая по предыдущему примеру формочка с двумя кнопками, вызывающие два разных метода. Разумеется, эти методы должны возвращать нечто, читабельное через браузер: удобно, если нечто основано на том же самом темплейте.

Методы должны быть сконфигурены, приведем кусок из configure.zcml:

          <pages
            permission="zope.ManageContent"
            class=".statistic.Statistic"
            for="..interfaces.ICachestore"
            >
            <page 
              name="statistic_view"
              attribute="statistic_view"
              menu="zmi_views" title="Statistic"
            />
            <page 
              name="clean"
              attribute="clean"
              />
            <page 
              name="regenerate_all"
              attribute="regenerate_all"
              />
            />
          </pages>

Эта конфигурация предполагает, что основанный на классе .statistic.Statistic адаптер вида, предоставляет три метода, ориентированных на вызов через веб: statistic_view (вид по умолчанию) и clean & regenerate_all : выполняющие действия по кнопкам в темплейте.

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

Модуль statistic.py:

          from zope.app.pagetemplate.viewpagetemplatefile \
            import ViewPageTemplateFile

          class Statistic(object) :

              # Темлейт который используется для возврата
              # результата во всех трех вызовах
              statistic_view = ViewPageTemplateFile("statistic.pt")

              # Обработчик кнопки regenerate_all:method
              def regenerate_all(self,*kv,**kw) :
                  count,elapsed = self.context.regenerate_all()
                  # Обратите внимание, результаты вычислений передаются
                  # в темлейт как ключевые аргументы: в темплейте они попадут
                  # в пространство имен options
                  return self.statistic_view(self,count=count,elapsed=elapsed,*kv,**kw)

              def clean(self,*kv,**kw) :
                  self.context.clean()
                  return self.statistic_view(self,*kv,**kw)

Вот такой простой класс позволяет эффективно запрограммировать на питоне всю логику взаимодействия адаптера вида с контент-объектом и оставить в темплейте только отображение.

Заключение:

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

          <tal:block tal:condition="request/forms/clean | nothing" 
              tal:content="view/clean"/> 

          <tal:block tal:condition="request/forms/regenerator_all | nothing" 
              tal:content="view/regenerator_all"/> 

Не длиннее, ни короче: дело вкуса, хотя на мой вкус, использование модификатора method оставляет больше свободы.


Powered by Plone CMS, the Open Source Content Management System