AdvancedZPT

Это введение в работу с шаблонами страниц Zope написано Evan Simpson. Оригинал статьи был опубликован на сайте zope.org. Перевод: Игорь Митин

Zope Page Templates - расширенное использование

Это введение в работу с шаблонами страниц Zope написано Evan Simpson. Оригинал статьи был опубликован на сайте zope.org

Evan Simpson

В первой статье мы описали как использовать шаблоны страниц и их несложный язык, TAL, для создания динамического представления. В этой статье мы покажем некоторые более расширенные возможности языка TAL. Во второй части статьи мы представим синтаксис выражений TAL, или TALES.

Совместное использование разных операторов

Как вы заметили в вашем примере шаблона, вы можете помещать более одного оператора TAL в один HTML тэг. Однамко, вы должны знать о трех ограничениях для такого использования.

  1. Только один оператор каждого типа может использоваться в пределах одного тэга. Дело в том, что HTML не позволяет нескольким атрибутам иметь одно и тоже имя, вы не можете иметь два оператора tal:define в пределах одного тэга.
  2. Оба оператора tal:content и tal:replace не могут использоваться в одном и том же тэге одновременно, так как эти функции, в таком случае, конфликтуют между собой.
  3. Последовательность в которой вы записываете TAL атрибуты в тэге не совпадает с последовательностью их выполнения. В каком бы порядке вы их не написали, выполняются они всегда в следующей последовательности: define, condition, repeat, content / replace, attributes.

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

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

      <div tal:repeat="p phrases">
        <p tal:define="n repeat/p/number">
        Phrase number <span tal:replace="n">1</span> is
        "<span tal:replace="p">Phrase</span>".</p>
      </div>

      <p tal:repeat="p phrases">
        <span tal:define="n repeat/p/number">
        Phrase number <span tal:replace="n">1</span> is
        "<span tal:replace="p">Phrase</span>".</span>
      </p>

Составные операторы

Если вам необходимо установить несколько атрибутов тэга, вы не сможете разместить несколько операторов tal:attributes и распределять их по другим тэгам тоже бесполезно.

Оба оператора tal:attributes и tal:define могут быть составными(иметь несколько частей). Вы разделяете эти части с помощью точки с запятой (';'), таким образом точка с запятой появляющаяся в выражении этих операторов должна быть экранированна, т.е. удвоенна (';;'). В этом примере устанавливаются значения двух атрибутов src и alt для картинки:

      <img src="/info/zope/zope-2/default.jpg"
           tal:attributes="src item/icon; alt item/id">

Здесь представленно определение нескольких переменных:

      <span tal:define="global logo here/logo.gif; ids here/objectIds">

Строковые выражения

Строковые выражения предоставляют вам простой путь для смешивания выражений с текстом. Весь текст рассположенный после string: просматриваеться с целью поиска выражений пути(path). Каждое выражение пути должно предваряться символом доллара ('$'). Если Если оно имеет более чем одну часть, или необходимо отделить его от следующего за ним текста, в этом случае необходимо заключить выражение в фигурные скобки ('{}'). В текст находящийся внутри значения атрибута может включать только двойные кавычки с использованием следующего синтаксиса """. Так как символ доллара служит для указания на выражения пути, то для использования данного символа внутри текста его необходимо удвоить ($$). Для примера:

      "string:Just text."
      "string:© $year, by Me."
      "string:Three ${vegetable}s, please."
      "string:Your name is ${user/getUserName}!"

Поведение выражений nocall

Обычно выражение пути пытается рендрить объект который оно запрашивает. Это означает, что если запрашиваемый объект является функцией, скриптом, методом или другой разновидностью исполняемого объекта, то выражение выполняет его и результат вызова объекта вставляет в страницу. Это не всегда соответствует тому что вы хотели бы. Для примера, если вы хотите поместить DTML документ в переменную чтобы узнать его свойства, вы не можете использовать обычное выражение пути, потому что оно рендрит документ в строковое представление.

Если поместить префикс nocall: в начале выражения пути, то он предотвратит рендеринг объекта и возвратит сам объект. Для примера:

      <span tal:define="doc nocall:here/aDoc"
            tal:content="string:${doc/id}: ${doc/title}">
      Id: Title</span>

Этот тип выражения также полезен, когда вы хотите определить переменную для функции или класса из модуля, для их использования в Python выражении.

Выражения Python

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

Другие встроенные переменные

Вы уже видели некоторые примеры с использованием встроенных переменных template, user, repeat и request. Здесь представлен полный список других встроенных переменных и информация для их использования:

  • nothing: фальшивое значение, аналогичное пустой строку, которое вы можете использовать в tal:replace или tal:content для очистки тэга или его содержимого. Если вы установите атрибут в nothing, этот атрибут будет удален из тэга(или не вставлен), в отличие от пустой строки.
  • default: специальное значение ничего не изменяющее при использовании в tal:replace, tal:content или tal:attributes. Оно оставляет текст шаблона в том месте где записан.
  • options: the keyword arguments, if any, that were passed to the template.
  • attrs: словарь из атрибутов текущего тэга в шаблоне. Ключами являются имена атрибутов, а значениями конкретные значения определенные в шаблоне.
  • root: корневой объект Zope. Используется для получения Zope объектов с фиксированным местоположением, не зависящим от того где расположен ваш шаблон и как он вызывается.
  • here: объект который вызывает шаблон. Часто это тоже самое что и контейнер, за исключением того что может изменяться если вы используете заимствование. Используйте его для получения объектов Zope которые могут быть найдены в различных местах в зависимости от того как шаблон был вызван.
  • container: контейнер (обычно папка) в которой хранится шаблон. Используйте ее для получения объектов Zope располагающихся относительно места где храниться шаблон.
  • modules: коллекция модулей Python доступных из шаблона. Смотрите написание Python выражений.

Альтертивные действия

Путь template/title гарантирует существование используемого title(т.к. является атрибутом самого шаблона), хотя бы даже если это просто пустая строка. Некоторые пути, такие как request/form/x, могут не существовать в момент рендеринга шаблона. Обычно в таком случает это приводит к ошибке(значение не существует).

Когда путь не существует, вы можете захотеть вернуться к предыдущему пути или вернуть какое-то другое значение вместо этого. Для примера, если request/form/x не существует, вы можете захотеть использовать here/x вместо этого. Вы можете сделать это определив список путей разделенных вертикальной чертой (|):

      <h4 tal:content="request/form/x | here/x">Header</h4>

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

Вы также можете проверить существование пути непосредственно с помощью префикса exists: в выражении. Префикс exists: в выражении возвращает true если путь существует и false если нет. Оба эти примера отображают сообщение об ошибке только если оно передано в запросе(объект request):

      <h4 tal:define="err request/form/errmsg | nothing"
          tal:condition="err" tal:content="err">Error!</h4>

      <h4 tal:condition="exists:request/form/errmsg"
          tal:content="request/form/errmsg">Error!</h4>

Элементы заглушки

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

      <tr tal:replace="nothing">
        <td>10213</td><td>Example Item</td><td>$15.34</td>
      </tr>

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

Вставка структур

Обычно операторы tal:replace и tal:content производят цитирование(трансляцию) вставляемого текста, для примера < транслируется в &lt;. Если вы хотите вставить не цитированный текст, вам необходимо предварить выражение ключевым словом structure. Получение переменной copyringht двумя способами:

      <span tal:replace="copyright">Copyright 2000</span>
      <span tal:replace="structure copyright">Copyright 2000</span>

...они генерируют "© 2001 By <b>Me</b>" и "© 2001 By Me" соответственно.

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

      <p tal:repeat="article topnewsitems"
         tal:content="structure article">A News Article</p>

Основы Python выражений

Язык Python является простым и выразительным. Если вы не встречались с ним ранее, вам необходимо прочитать одно из отличных учебных пособий или введений доступных на сайте http://www.python.org.

Шаблон страницы может содержать любые Python выражения, соответствующие синтаксису языка Python. Вы не можете использовать такие операторы как if и while и применять Zope ограничения безопасности.

Сравнение

Выражения Python практически необходимо размещать в операторах tal:condition. Обычно вам необходимо сравнить две строки или два числа и в зависимости от результата выбрать какой либо путь выполнения. Вы можете использовать операторы сравнения '<'(меньще чем), '>'(больше чем), '=='(эквивалентно) и '!='(не эквивалентно). Вы также можете использовать булевы операторы and, not, и or. Для примера:

        <p tal:repeat="widget widgets">
          <span tal:condition="python:widget.type == 'gear'>
          Gear #<span tal:replace="repeat/widget/number>1</span>:
          <span tal:replace="widget/name">Name</span>
          </span>
        </p>

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

        You <span tal:define="name user/getUserName"
             tal:replace="python:test(name=='Anonymous User',
                                     'need to log in', default)">
              are logged in as
              <span tal:replace="name">Name</span>
            </span>

        <tr tal:define="oddrow repeat/item/odd"
            tal:attributes="class python:test(oddrow, 'oddclass',
                                              'evenclass')">

Использование других типов выражений

Вы можете использовать и другие разновидности выражений внутри Python выражений. Каждая разновидность имеет соответствующую функцию с тем же именем, включая path(), string(), exists() и nocall(). Это позволяет вам записывать выражения подобные этим:

        "python:path('here/%s/thing' % foldername)"
        "python:path(string('here/$foldername/thing'))"
        "python:path('request/form/x') or default"

В заключительном примере есть незначительные отличия с выражение пути "request/form/x | default", однако работает он также, т.е. если "request/form/x" не существует или возвращает false то используется текст по умолчанию.

Получение объектов Zope

Сервер Zope содержит много различных специализированных объектов работающих совместно. Ваши шаблоны страниц могут использовать скрипты, SQL методы, каталоги и ваши собственные объекты контента. Для их использования вам необходимо знать как получить к ним доступ.

Свойства объекта обычно являются его атрибутами, т.е. вы можете обратиться к свойству шаблона title используя выражение "template.title". Большинство объектов Zope поддерживают заимствование, что позволяет вам получить атрибуты из объектов "родителей". Эта методика используется в выражении Python "here.Control_Panel" для заимствования объекта панели управления из корня Zope. Методы объекта подобны атрибутам, как в "here.objectIds" и "request.set". К объектам содержащимся в папке можно получить доступ как к атрибутам папки, часто они имеют идентификаторы(Id) не являющиеся правильными Python идентификатороми, в таком случае вы не можете использовать обычную нотацию. Для примера, вместо "here.penguin.gif", вы должны записать "getattr(here, penguin.gif)".

Некоторые объекты, такие как request, modules и Zope папки поддерживают доступ к элемента. Некоторые примеры этого:

      request['URL'], modules['math'], and here['thing']

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

Как показано в предыдущей части, выражения пути позволяют вам игнорировать детали получения объектов. Вы можете записать "here/images/penguin.gif" вместо "python:getattr(here.images, penguin.gif)", и "request/form/x" вместо "python:request.form[x]?".

The tradeoff is that path expressions don't allow you to specify those details. For instance, if you have a form variable named "get", you must write "python:request.form[get]?", since "request/form/get" will evaluate to the "get" method of the form dictionary. Компромис с выражением пути не позволяет вам определить некоторые детали. Для примера, если вы имеет переменную формы с именем "get", вы должны записать "python:request.form[get]?", т.к. в случае "request/form/get" будет выполнен "get" метод из словаря формы.

Использование скриптов

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

Каждый скрипт содержит список параметров которые он ожидает получить в тот момент когда его вызывают. Если этот список пуст, тогда вы можете использовать этот скрипт в обычном выражении пути. Иначе, вам необходимо использовать Python выражение подобное этим:

      "python:here.myscript(1, 2)"
      "python:here.myscript('arg', foo=request.form['x'])"

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

      getPerson returns this: {'name': 'Fred', 'age': 25}

      <span tal:define="person here/getPerson"
            tal:replace="string:${person/name} is ${person/age}">
      Name is 30</span> years old.

Вызов DTML

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

Когда ZPublisher? опубликовывает DTML объект, он передает ему контекст как объект client, и объект REQUEST в виде "карты". Когда один DTML метод вызывает другого, он передает ему свою собственную область имен как карту(словарь) и не передает объект client.

Если вы используете выражение пути для рендеринга DTML объекта, ему передается пространство имен c переменными request, here и template. Эта методика обеспечивает использование тех же самых имен, как если бы DTML публиковался в том же самом контексте что и шаблон, плюс имена переменных определенных в шаблоне.

Модули Python

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

Некоторые модули, включая "math" и "string", доступны в выражениях Python по умолчанию. Для примера, вы можете получить значение числа pi из модуля math просто записав "python:math.pi". Однако, для доступа к нему из выражения пути вам необходимо использовать переменную modules. В этом случае, вы должны использовать "modules/math/pi". Пожалуйста обратитесь к книге по Zope или к руководству по DTML за дополнительной информацией относительно этих модулей.

Модуль "string" является скрытым в выражениях Python, поэтому вам необходимо получать доступ к нему через переменную modules. Вы можете сделать это непосредственно в выражении которое его использует или определить глобальную переменную для него, как в примере:

      tal:define="global mstring modules/string"
      tal:replace="python:mstring.join(slist, ':')"

Модули могут группироваться в пакеты, это является простым способом именования и организации связанных модулей. Для примера, Zope Python-based Scripts предоставляют коллекцию модулей в "PythonScripts?" подпакете пакета Zope "Products".

В частности, модуль "standard" в пакете предоставляет множество полезных функций форматирования которые являются стандартными для DTML тэга var. Полным именем этого модуля является "Products.PythonScripts?.standard", таким образом вы должны получать доступ к этому модулю используя один из двух нижеприведенных операторов:

      tal:define="pps modules/Products.PythonScripts.standard"
      tal:define="pps python:modules['Products.PythonScripts.standard']"

К большинство модулей Python невозможно получить доступ из шаблонов страниц, DTML методов или из скриптов до тех пор пока вы не добавить разрешение для них в "Zope security". Это выходит за границы данного документа, и для дополнительной информации об этом необходимо обратиться к руководству Zope Security Guide.

По материалам http://zope.org
Публикуется с разрешения переводчика:
Igor Mitin
i_mitin@mail.ru