Chapter 5: Представления

Представления

views
template language
HTML

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

Назначением представления является внедрение кода (Python) в HTML-документ. В целом, это создает некоторые проблемы:

  • Каким образом встроенный код должен быть экранирован?
  • Отступы должны быть основаны на правилах Python или HTML?

web2py использует {{ ... }} чтобы выделить Python код, внедренный в HTML. Преимуществом использования фигурных скобок вместо угловых скобок является то, что они прозрачны для всех распространенных HTML-редакторов. Это позволяет разработчику использовать эти редакторы для создания web2py представлений. Данные разделители могут быть изменены, например:

1
response.delimiters = ('<?','?>')

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

Так как разработчик встраивает код Python в HTML, то отступы в документе должны быть в соответствии с правилами HTML, а не с правилами Python. Поэтому, мы допускаем Python код без отступов внутри {{...}} тегов. Поскольку Python обычно использует отступы для разделения блоков кода, то нам нужен другой способ разграничить их; именно поэтому язык шаблонов web2py использует ключевое слово Python pass.

Блок кода начинается со строки, которая заканчивается двоеточием, и заканчивается строкой, которая начинается с pass. Ключевое слово pass не является обязательным, когда конец блока очевиден из контекста.

Вот пример:

1
2
3
4
5
6
7
{{
if i == 0:
response.write('i is 0')
else:
response.write('i is not 0')
pass
}}

Обратите внимание, что pass является ключевым словом Python, а не ключевым словом web2py. Некоторые редакторы Python, такие как Emacs, используют ключевое слово pass для обозначения разделения блоков и используют его для повторного отступа кода автоматически.

Язык шаблонов web2py делает то же самое. Когда он находит что-то вроде:

1
2
3
<html><body>
{{for x in range(10):}}{{=x}}hello<br />{{pass}}
</body></html>

он переводит его в программу:

1
2
3
4
5
response.write("""<html><body>""", escape=False)
for x in range(10):
    response.write(x)
    response.write("""hello<br />""", escape=False)
response.write("""</body></html>""", escape=False)

response.write writes to the response.body.

При возникновении ошибки в представлении web2py, в отчете об ошибке показывается сгенерированный код представления, а не фактическое представление, написанное разработчиком. Это помогает разработчику отлаживать код путем выделения фактического кода, который выполняется (то, что может быть отлажено с помощью редактора HTML или инспектора DOM браузера).

Также отметим, что:

1
{{=x}}

генерирует

response.write
escape

1
response.write(x)

Переменные впрыскивается в HTML код и таким спосбом экранируются (escaped) по умолчанию. Экранирование игнорируется, если x является объектом XML, даже если экранирование escape установлено на True.

Ниже приведен пример, который вводит H1 помощника:

1
{{=H1(i)}}

который переводится в:

1
response.write(H1(i))

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

Метод response.write(obj, escape=True) принимает два аргумента, объект для записи и разрешение на экранирование (установлено на True по умолчанию). Если obj имеет метод .xml(), то он вызывается и результат записывается в тело ответа (escape аргумент игнорируется). В противном случае он использует метод объекта __str__ для его сериализации и, если аргумент escape установлен на True, экранировании его. Все встроенные вспомогательные объекты (H1 в примере) являются объектами, которые знают, как сериализовать себя через .xml() метод.

Это все делается прозрачно. Вам никогда не понадобиться (и не следует) вызывать метод response.write в явном виде.

Основной синтаксис

Язык шаблонов web2py поддерживает все управляющие структуры Python. Здесь мы приводим некоторые примеры каждой из них. Они могут быть вложены в соответствии с обычной практикой программирования.

for...in

for

В шаблонах вы сможете циклически перебрать итерируемый объект:

1
2
3
4
{{items = ['a', 'b', 'c']}}
<ul>
{{for item in items:}}<li>{{=item}}</li>{{pass}}
</ul>

который производит:

1
2
3
4
5
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>

Здесь item является любым итерируемым объектом, таким как список Python, кортеж Python, объект Rows или любой другой объект, который реализуется в качестве итератора. Элементы, выводимые на экран, сперва сериализуются и экранируются.

while

while

Вы можете создать цикл, используя ключевое слово while:

1
2
3
4
{{k = 3}}
<ul>
{{while k > 0:}}<li>{{=k}}{{k = k - 1}}</li>{{pass}}
</ul>

который производит:

1
2
3
4
5
<ul>
<li>3</li>
<li>2</li>
<li>1</li>
</ul>

if...elif...else

if
elif
else

Вы можете использовать условные операторы:

1
2
3
4
5
6
7
8
{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 2:}}is odd{{else:}}is even{{pass}}
</h2>

который производит:

1
2
3
<h2>
45 is odd
</h2>

Поскольку очевидно, что else закрывает первый if блок, то нет никакой необходимости в объявлении pass, использованием которого было бы неправильным. Тем не менее, вы должны явно закрыть else блок с pass.

Напомним, что в Python "else if" пишется elif, как в следующем примере:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 4 == 0:}}is divisible by 4
{{elif k % 2 == 0:}}is even
{{else:}}is odd
{{pass}}
</h2>

Она производит:

1
2
3
<h2>
64 is divisible by 4
</h2>

try...except...else...finally

try
except
else
finally

Кроме того, можно использовать try...except операторы в представлениях с одной оговоркой. Рассмотрим следующий пример:

1
2
3
4
5
6
7
8
9
{{try:}}
Hello {{= 1 / 0}}
{{except:}}
division by zero
{{else:}}
no division by zero
{{finally}}
<br />
{{pass}}

Он будет производить следующий вывод:

1
2
3
Hello
division by zero
<br />

Этот пример показывает, что все выходные данные, генерируемые до возникновения исключения, визуализируются (в том числе выводные данные, которые предшествовали исключению) внутри блока try. "Hello" пишется, потому что он предшествует исключению.

Оператор def...return

def
return

Язык шаблонов web2py позволяет разработчику определять и реализовывать функции, которые могут возвращать любой объект Python или text/html строку. Здесь мы рассмотрим два примера:

1
2
3
4
{{def itemize1(link): return LI(A(link, _href="http://" + link))}}
<ul>
{{=itemize1('www.google.com')}}
</ul>

производит следующий вывод:

1
2
3
<ul>
<li><a href="http:/www.google.com">www.google.com</a></li>
</ul>

Функция itemize1 возвращает вспомогательный объект, который вставляется в том месте, где вызывается функция.

Рассмотрим теперь следующий код:

1
2
3
4
5
6
{{def itemize2(link):}}
<li><a href="http://{{=link}}">{{=link}}</a></li>
{{return}}
<ul>
{{itemize2('www.google.com')}}
</ul>

Она производит точно такой же результат, как описано выше. В этом случае функция itemize2 представляет собой кусок HTML, который идет на замену web2py тега, где вызывается функция. Заметьте, что нет '=' перед вызовом itemize2, так как функция не возвращает текст, но он записывает его непосредственно в ответ.

Существует один нюанс: функции, определенные внутри представления должны завершаться с оператором return, или автоматическая расстановка отступов потерпит неудачу.

HTML Помощники

helpers

Рассмотрим следующий код в представлении:

1
{{=DIV('this', 'is', 'a', 'test', _id='123', _class='myclass')}}

он визуализируется как:

1
<div id="123" class="myclass">thisisatest</div>

DIV является классом помощника, то есть классом, который может быть использован для программного построения HTML кода. Он соответствует HTML тегу <div>.

Позиционные аргументы интерпретируются как объекты, содержащиеся между открывающим и закрывающим тегами. Именованные аргументы, которые начинаются с символа подчеркивания интерпретируются как HTML атрибуты тега (без подчеркивания). Некоторые помощники также имеют именованные аргументы, которые не начинаются с подчеркивания; эти аргументы специфичных тегов.

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

1
2
3
4
5
{{
contents = ['this','is','a','test']
attributes = {'_id':'123', '_class':'myclass'}
=DIV(*contents,**attributes)
}}

(производит такой же результат, как и раньше).

Следующий набор помощников:

A, B, BEAUTIFY, BODY, BR, CAT, CENTER, CODE, COL, COLGROUP, DIV, EM, EMBED, FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML, I, IFRAME, IMG, INPUT, LABEL, LEGEND, LI, LINK, MARKMIN, MENU, META, OBJECT, ON, OL, OPTGROUP, OPTION, P, PRE, SCRIPT, SELECT, SPAN, STYLE, TABLE, TAG, TBODY, TD, TEXTAREA, TFOOT, TH, THEAD, TITLE, TR, TT, UL, URL, XHTML, XML, embed64, xmlescape

может быть использован для построения сложных выражений, которые затем могут быть сериализованы в XML [xml-w] [xml-o]. Например:

1
{{=DIV(B(I("hello ", "<world>")), _class="myclass")}}

визуализируется:

1
<div class="myclass"><b><i>hello &lt;world&gt;</i></b></div>

Помощники также могут быть сериализованы в строки, также как и с __str__ и xml методами:

>>> print str(DIV("hello world"))
<div>hello world</div>
>>> print DIV("hello world").xml()
<div>hello world</div>
Объектная Модель Документа (DOM)

Механизм помощников в web2py больше, чем система для генерации HTML без конкатенации строк. Он обеспечивает на стороне сервера представление объектной модели документа (DOM).

К компонентам внутри помощников можно обращаться по номеру их позиции, и помощники действуют как списки в отношении своих компонентов:

1
2
3
4
5
6
7
8
>>> a = DIV(SPAN('a', 'b'), 'c')
>>> print a
<div><span>ab</span>c</div>
>>> del a[1]
>>> a.append(B('x'))
>>> a[0][0] = 'y'
>>> print a
<div><span>yb</span><b>x</b></div>

К атрибутам помощников можно обращаться по имени, и помощники действуют как словари в отношении своих атрибутов:

1
2
3
4
5
>>> a = DIV(SPAN('a', 'b'), 'c')
>>> a['_class'] = 's'
>>> a[0]['_class'] = 't'
>>> print a
<div class="s"><span class="t">ab</span>c</div>

Обратите внимание, что полный набор компонентов, может быть доступен через список, вызываемый через a.components, и полный набор атрибутов может быть доступен через словарь, вызываемый через a.attributes. Таким образом, a[i] эквивалентно a.components[i], когда i является целым, и a[s] эквивалентно a.attributes[s], когда s является строкой.

Обратите внимание на то, что атрибуты HTML-тега передаются как ключевые слова в аргументах помощника. В некоторых случаях, имена атрибутов HTML-тега могут включать в себя специальные символы, которые не разрешены в идентификаторах Python (например, дефис) и, следовательно, не могут быть использованы в качестве ключевых слов именованных аргументов. Например:

1
DIV('text', _data-role='collapsible')

не будет работать, потому что "_data-role" включает в себя дефис, который будет производить ошибку синтаксиса Python.

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

1
2
>>> print DIV('text', data={'role': 'collapsible'})
<div data-role="collapsible">text</div>

или вы можете взамен этого передать атрибуты в виде словаря и использовать Python обозначение ** для аргументов функции, что сопоставит словарь: пары (ключ:значение) в множество ключевых аргументов:

1
2
>>> print DIV('text', **{'_data-role': 'collapsible'})
<div data-role="collapsible">text</div>

Обратите внимание, что более сложные записи будут вводить символьные сущности HTML, но они, тем не менее, будут работать, например

1
2
>>> print DIV('text', data={'options':'{"mode":"calbox", "useNewStyle":true}'})
<div data-options="{&quot;mode&quot;:&quot;calbox&quot;, &quot;useNewStyle&quot;:true}">text</div>

Кроме того, можно динамически создавать специальные тэги:

1
2
>>> print TAG['soap:Body']('whatever',**{'_xmlns:m':'http://www.example.org'})
<soap:Body xmlns:m="http://www.example.org">whatever</soap:Body>

XML

XML

XML это объект, используемый для инкапсуляции текста, который не должен экранироваться. Текст может содержать или не содержать допустимый XML. Например, он может содержать JavaScript.

Текст в этом примере экранируется:

1
2
>>> print DIV("<b>hello</b>")
&lt;b&gt;hello&lt;/b&gt;

используя XML вы можете предотвратить экранирование:

1
2
>>> print DIV(XML("<b>hello</b>"))
<b>hello</b>

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

1
2
>>> print XML('<script>alert("unsafe!")</script>')
<script>alert("unsafe!")</script>

Неэкранированный исполняемый ввод, такой как этот (например, введенный в тело комментария к блогу) является небезопасным, так как он может быть использован для создания Cross Site Scripting (XSS) атак против других посетителей страницы.

sanitize

Web2py XML помощник может санировать (sanitize) наш текст, чтобы предотвратить инъекции и экранировать все теги, за исключением тех, которые вы явно разрешили. Вот пример:

1
2
>>> print XML('<script>alert("unsafe!")</script>', sanitize=True)
&lt;script&gt;alert(&quot;unsafe!&quot;)&lt;/script&gt;

Конструкторы XML по умолчанию, безопасным способом рассматривают содержание некоторых тегов и некоторые из их атрибутов. Вы можете переопределить значения по умолчанию, используя дополнительные аргументы permitted_tags и allowed_attributes. Вот значения по умолчанию необязательных аргументов XML помощника.

1
2
3
4
5
XML(text, sanitize=False,
    permitted_tags=['a', 'b', 'blockquote', 'br/', 'i', 'li',
       'ol', 'ul', 'p', 'cite', 'code', 'pre', 'img/'],
    allowed_attributes={'a':['href', 'title'],
       'img':['src', 'alt'], 'blockquote':['type']})

Встроенные помощники

A

Этот помощник используется для построения ссылок.

A
1
2
3
>>> print A('<click>', XML('<b>me</b>'),
            _href='http://www.web2py.com')
<a href='http://www.web2py.com'>&lt;click&gt;<b>me/b></a>

Вместо _href вы можете передать URL с помощью callback аргумента. Например, в представлении:

{{=A('click me', callback=URL('myaction'))}}

и эффектом нажатия на ссылку будет Ajax вызов "myaction" вместо перенаправления. В этом случае при необходимости можно указать еще два аргумента: target и delete:

{{=A('click me', callback=URL('myaction'), target="t")}}
<div id="t"><div>

и ответ на обратный вызов Ajax будет храниться в DIV с идентификатором, равным "t".

<div id="b">{{=A('click me', callback=URL('myaction'), delete='div#b")}}</div>

и после ответа, наиболее подходящий тег "div#b" будет удален. В этом случае кнопка будет удалена. Типичным применением является:

{{=A('click me', callback=URL('myaction'), delete='tr")}}

в таблице. При нажатии кнопки будет выполнять обратный вызов и удаление строки таблицы.

callback и delete могут быть скомбинированы.

Помощник А принимает специальный аргумент с именем cid. Он работает следующим образом:

1
2
{{=A('linked page', _href='http://example.com', cid='myid')}}
<div id="myid"></div>

и нажатие на ссылку заставляет содержание загрузиться в div. Это аналогичный, но более мощный, чем предыдущий вариант синтаксиса, так как он предназначен для обновления компонентов страницы. Мы обсудим применение cid более подробно в главе 12, в контексте компонентов.

Эти функции требуют Ajax и JQuery "static/js/web2py_ajax.js", которые автоматически включаются путем размещения {{include 'web2py_ajax.html'}} в заголовке макета. "views/web2py_ajax.html" определяет некоторые переменные на основе request и включает в себя все необходимые JS и CSS файлы.

B

B

Этот помощник делает свое содержимое жирным шрифтом.

1
2
>>> print B('<hello>', XML('<i>world</i>'), _class='test', _id=0)
<b id="0" class="test">&lt;hello&gt;<i>world</i></b>

BODY

BODY

Этот помощник делает тело страницы.

1
2
>>> print BODY('<hello>', XML('<b>world</b>'), _bgcolor='red')
<body bgcolor="red">&lt;hello&gt;<b>world</b></body>

BR

BR

Этот помощник создает разрыв строки.

1
2
>>> print BR()
<br />

Обратите внимание на то, что помощники могут быть повторены с помощью оператора умножения:

1
2
>>> print BR()*5
<br /><br /><br /><br /><br />

CAT

CAT

Этот помощник сцепляет других помощников, так же как TAG[''].

1
2
>>> print CAT('Here is a ', A('link',_href=URL()), ', and here is some ', B('bold text'), '.')
Here is a <a href="/app/default/index">link</a>, and here is some <b>bold text</b>.

CENTER

CENTER

Этот помощник центрует свое содержание.

1
2
3
>>> print CENTER('<hello>', XML('<b>world</b>'),
>>>              _class='test', _id=0)
<center id="0" class="test">&lt;hello&gt;<b>world</b></center>

CODE

CODE

Этот помощник выполняет подсветку синтаксиса для кода Python, C, C++, HTML и web2py, и является предпочтительнее чем PRE для листинга программного кода. CODE также имеет возможность создавать ссылки на документацию API web2py.

Ниже приведен пример подсветки разделов кода Python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print CODE('print "hello"', language='python').xml()
<table><tr valign="top"><td style="width:40px; text-align: right;"><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
        background-color: #E0E0E0;
        color: #A0A0A0;
    ">1.</pre></td><td><pre style="
        font-size: 11px;
        font-family: Bitstream Vera Sans Mono,monospace;
        background-color: transparent;
            margin: 0;
            padding: 5px;
            border: none;
            overflow: auto;
    "><span style="color:#185369; font-weight: bold">print </span>
    <span style="color: #FF9966">"hello"</span></pre></td></tr>
</table>

Вот подобный пример для HTML

1
2
3
>>> print CODE(
>>>   '<html><body>{{=request.env.remote_add}}</body></html>',
>>>   language='html')
1
2
3
<table>...<code>...
<html><body>{{=request.env.remote_add}}</body></html>
...</code>...</table>

Эти аргументы по умолчанию для помощника CODE:

1
CODE("print 'hello world'", language='python', link=None, counter=1, styles={})

Поддерживаемыми значениями для аргумента language являются "python", "html_plain", "c", "cpp", "web2py" и "html". В "html" язык интерпретирует {{ и }} теги, как "web2py" код, в то время как "html_plain" нет.

Если задано значение link, например "/examples/global/vars/", то web2py API ссылки в коде связываются с документацией по URL ссылке. Например "request" будет связан с "/examples/global/vars/request". В приведенном выше примере, ссылка URL обрабатывается через "vars" действием в контроллере "global.py", который распространяется как часть web2py приложения "examples".

Аргумент counter используется для нумерации строк. Он может быть установлен в любом из трех различных значений. Это может быть None при отсутствии номеров строки, числовым значением, определяющим начальный номер или строку. Если счетчик установлен в string, то он интерпретируется как подсказка, и нет имеет номеров строки.

Аргумент styles немного сложнее. Если вы посмотрите на сгенерированный выше HTML-код, то он содержит таблицу с двумя столбцами, и каждый столбец имеет свой собственный стиль, объявленный внутри строк с помощью CSS. Атрибуты styles позволяют переопределить эти два стиля CSS. Например:

1
{{=CODE(...,styles={'CODE':'margin: 0;padding: 5px;border: none;'})}}

Атрибут styles должен быть словарем, и он допускает два возможных ключа: CODE для стиля актуального кода, и LINENUMBERS для стиля в левой колонке, которая содержит номера строк. Имейте ввиду, что эти стили полностью заменяют стандартные стили, а не просто добавляются к ним.

COL

COL
1
2
>>> print COL('a','b')
<col>ab</col>

COLGROUP

COLGROUP
1
2
>>> print COLGROUP('a','b')
<colgroup>ab</colgroup>

DIV

Все помощники, кроме XML, являются производными от DIV и наследуют его основные методы.

DIV
1
2
>>> print DIV('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<div id="0" class="test">&lt;hello&gt;<b>world</b></div>

EM

Подчеркивает свое содержимое.

EM
1
2
>>> print EM('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<em id="0" class="test">&lt;hello&gt;<b>world</b></em>

FIELDSET

FIELDSET

Используется для создания поля ввода вместе с пометкой (label).

1
2
>>> print FIELDSET('Height:', INPUT(_name='height'), _class='test')
<fieldset class="test">Height:<input name="height" /></fieldset>

FORM

FORM

Это один из наиболее важных помощников. В простой форме, он просто создает тэг <form>...</form>, но поскольку помощники являются объектами и имеют знания о том, что они содержат, то они могут обрабатывать предоставленные формы (например, выполнить проверку полей). Этот вопрос будет обсуждаться подробно в Главе 7.

1
2
3
>>> print FORM(INPUT(_type='submit'), _action='', _method='post')
<form enctype="multipart/form-data" action="" method="post">
<input type="submit" /></form>

Атрибут "enctype" явялется "multipart/form-data" по умолчанию.

hidden

Конструктор от FORM, и от SQLFORM, также может принимать специальный аргумент с именем hidden. Когда словарь передается как hidden, то его элементы переводятся в "скрытые" поля ввода (INPUT fields). Например:

1
2
3
>>> print FORM(hidden=dict(a='b'))
<form enctype="multipart/form-data" action="" method="post">
<input value="b" type="hidden" name="a" /></form>

H1, H2, H3, H4, H5, H6

H1

Это помощники для заголовков и подзаголовков параграфа:

1
2
>>> print H1('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<h1 id="0" class="test">&lt;hello&gt;<b>world</b></h1>

HEAD

Для тэга HEAD в HTML странице.

HEAD
1
2
>>> print HEAD(TITLE('<hello>', XML('<b>world</b>')))
<head><title>&lt;hello&gt;<b>world</b></title></head>

HTML

HTML
XHTML

Этот помощник немного отличается. В дополнение к созданию <HTML> тегов, он помещает тэг со строкой типа документа doctype [xhtml-w,xhtml-o,xhtml-school] .

1
2
3
4
>>> print HTML(BODY('<hello>', XML('<b>world</b>')))
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<html><body>&lt;hello&gt;<b>world</b></body></html>

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

1
HTML(..., lang='en', doctype='transitional')

где doctype может быть 'strict', 'transitional', 'frameset', 'html5', или полная строка doctype.

XHTML

XHTML

XHTML похож на HTML, но он создает XHTML doctype вместо этого.

1
XHTML(..., lang='en', doctype='transitional', xmlns='http://www.w3.org/1999/xhtml')

где doctype может быть 'strict', 'transitional', 'frameset', или полная строка doctype.

HR

HR

Этот помощник создает горизонтальную строку в HTML-странице

1
2
>>> print HR()
<hr />

I

I

Этот помощник делает свое содержимое курсивом.

1
2
>>> print I('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<i id="0" class="test">&lt;hello&gt;<b>world</b></i>

IFRAME

Этот помощник включает в себя другую веб-страницу, на текущей странице. URL-адрес на другую страницу задается с помощью атрибута "_src".

IFRAME
1
2
>>> print IFRAME(_src='http://www.web2py.com')
<iframe src="http://www.web2py.com"></iframe>

IMG

IMG

Он может быть использован для встраивания изображений в HTML:

1
2
>>> IMG(_src='http://example.com/image.png',_alt='test')
<img src="http://example.com/image.ong" alt="rest" />

Здесь сочетание из A, IMG и URL-помощников для включения статического изображения со ссылкой:

1
2
3
4
5
>>> A(IMG(_src=URL('static','logo.png'), _alt="My Logo"),
      _href=URL('default','index'))
<a href="/myapp/default/index">
  <img src="/myapp/static/logo.png" alt="My Logo" />
</a>

INPUT

INPUT

Создает тег <input.../>. Тег ввода input не может содержать другие вложенные теги и поэтому закрывается через /> вместо >. Тег ввода input имеет необязательный атрибут _type который может быть установлен в "text" (по умолчанию), "submit", "checkbox", или "radio".

1
2
>>> print INPUT(_name='test', _value='a')
<input value="a" name="test" />

Он также принимает необязательный специальный аргумент с именем "value", который отличается от "_value". "_value" устанавливает значение по умолчанию для поля ввода; "value" устанавливает текущее значение поля ввода. Для ввода типа "text", "value" переопределяет "_value" (тип "text" используется по умолчанию):

1
2
>>> print INPUT(_name='test', _value='a', value='b')
<input value="b" name="test" />

Для радио-кнопок, INPUT выборочно устанавливает атрибут "checked" (т.е. если его текущее значение "value" равно значению по умолчанию "_value"):

radio
1
2
3
4
5
>>> for v in ['a', 'b', 'c']:
>>>     print INPUT(_type='radio', _name='test', _value=v, value='b'), v
<input value="a" type="radio" name="test" /> a
<input value="b" type="radio" checked="checked" name="test" /> b
<input value="c" type="radio" name="test" /> c

а так же для флажков:

checkbox
1
2
3
4
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=True)
<input value="a" type="checkbox" checked="checked" name="test" />
>>> print INPUT(_type='checkbox', _name='test', _value='a', value=False)
<input value="a" type="checkbox" name="test" />

LABEL

Используется для создания тега пометки LABEL для INPUT поля.

LABEL
1
2
>>> print LABEL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<label id="0" class="test">&lt;hello&gt;<b>world</b></label>

LEGEND

Он используется для создания тега легенды legend для поля в форме.

LEGEND
1
2
>>> print LEGEND('Name', _for='myfield')
<legend for="myfield">Name</legend>

LI

Он создает элемент списка и должен содержаться в UL или OL тегах.

LI
1
2
>>> print LI('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<li id="0" class="test">&lt;hello&gt;<b>world</b></li>

META

Используется для построения META тегов в HTML заголовке. Например:

META
1
2
>>> print META(_name='security', _content='high')
<meta name="security" content="high" />

MARKMIN

Реализует синтаксис markmin вики. Он преобразует входной текст в выходной HTML в соответствии с правилами, описанными в примере markmin ниже:

MARKMIN
1
2
>>> print MARKMIN("это **жирный** или ''курсивом'', а это [[ссылка http://web2py.com]]")
<p>это <b>жирный</b> или <i>курсивом</i>, а это <a href="http://web2py.com">ссылка</a></p>

Синтаксис markmin описан в этом файле, который поставляется с web2py:

1
http://127.0.0.1:8000/examples/static/markmin.html

Вы можете использовать markmin для генерации HTML, LaTeX и PDF-документов:

m = "Hello **world** [[link http://web2py.com]]"
from gluon.contrib.markmin.markmin2html import markmin2html
print markmin2html(m)
from gluon.contrib.markmin.markmin2latex import markmin2latex
print markmin2latex(m)
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
print markmin2pdf(m) # requires pdflatex

(помощник MARKMIN является сокращением для markmin2html)

Вот основной синтаксис праймера:

ИсточникВывод
# ОглавлениеОглавление
## РазделРаздел
### ПодразделПодраздел
**Жирный**Жирный
''Курсив''Курсив
``дословный``дословный
http://google.comhttp://google.com
http://...<a href="http://...">http:...</a>
http://...png<img src="http://...png" />
http://...mp3<audio src="http://...mp3"></audio>
http://...mp4<video src="http://...mp4"></video>
qr:http://...<a href="http://..."><img src="qr code"/></a>
embed:http://...<iframe src="http://..."></iframe>
[[нажми меня #мойякорь]]нажми меня
[[мойякорь]]Создание якоря для ссылки
$$\int_a^b sin(x)dx$$
MARKMIN ссылки

Ссылки принимают эту форму: [[link display text <link>]] <link> может быть якорем, например #мойякорь или URI, например http://www.web2py.com или относительная ссылка, например [[Смотри главу ../08]] или [[Смотри главу 8 ../08#мойякорь]]

Просто включаются ссылки на изображение, видео или аудио файлы без необходимости разметки результата в соответствующем изображения, видео или аудио файле, которая включается автоматически (для аудио и видео он использует HTML <audio> и <video> теги).

Добавление ссылки с префиксом qr:, такой как

qr:http://web2py.com

Результаты в соответствующем QR-коде встраивают и связывают указанный URL.

Добавление ссылки с префиксом embed:, такой как

embed:http://www.youtube.com/embed/x1w8hKTJ2Co

Результаты на странице будут внедрены, в этом случае видео YouTube является внедренным.

Изображения также могут быть внедрены со следующим синтаксисом:

[[image-description http://.../image.png right 200px]]
MARKMIN списки и таблицы

Неупорядоченные списки с:

- one
- two
- three

Упорядоченные списки с:

+ one
+ two
+ three

и таблицы с:

----------
 X | 0 | 0
 0 | X | 0
 0 | 0 | 1
----------
MARKMIN расширение

Синтаксис MARKMIN также поддерживает блоки цитат, аудио и видео теги HTML5, выравнивание изображения, пользовательские CSS, и он может быть расширен:

1
MARKMIN("``abab``:custom", extra=dict(custom=lambda text: text.replace('a','c'))

генерирует

1
'cbcb'

Пользовательские блоки разделяются ``...``:<key> и они визуализируются через функцию, переданную в качестве значения соответствующего ключа в дополнительном словаре аргумента MARKMIN. Имейте в виду, что может понадобиться экранирование вывода функции, чтобы предотвратить XSS.

OBJECT

Используется для внедрения объектов (например, Flash Player) в HTML.

OBJECT
1
2
3
>>> print OBJECT('<hello>', XML('<b>world</b>'),
>>>              _src='http://www.web2py.com')
<object src="http://www.web2py.com">&lt;hello&gt;<b>world</b></object>

OL

Это стойка для упорядоченных списков. Список должен содержать LI теги. Аргументы OL, которые не являются LI объектами автоматически заключаются в <li>...</li> теги.

OL
1
2
>>> print OL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ol id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ol>

ON

Используется для обратной совместимости, и является просто псевдонимом для True. Он используется исключительно для флажков и является устаревшим, использование True является более Питоновским.

ON
1
2
>>> print INPUT(_type='checkbox', _name='test', _checked=ON)
<input checked="checked" type="checkbox" name="test" />

OPTGROUP

Позволяет сгруппировать несколько опций в SELECT и пригодится для настройки полей с помощью CSS.

OPTGROUP
1
2
3
4
5
6
7
8
>>> print SELECT('a', OPTGROUP('b', 'c'))
<select>
  <option value="a">a</option>
  <optgroup>
    <option value="b">b</option>
    <option value="c">c</option>
  </optgroup>
</select>

OPTION

Используется только как часть SELECT/OPTION комбинации.

OPTION
1
2
>>> print OPTION('<hello>', XML('<b>world</b>'), _value='a')
<option value="a">&lt;hello&gt;<b>world</b></option>

Как и в случае INPUT, web2py проводить различие между "_value" (значением из OPTION), и "value" (текущим значением, заключенным в выборе). Если они равны, то опция помечается как выбранная "selected".

selected
1
2
3
4
5
>>> print SELECT('a', 'b', value='b'):
<select>
<option value="a">a</option>
<option value="b" selected="selected">b</option>
</select>

P

P

Используется для маркировки параграфа.

1
2
>>> print P('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<p id="0" class="test">&lt;hello&gt;<b>world</b></p>

PRE

PRE

Генерирует тег <pre>...</pre> для отображения предварительно отформатированного текста. Помощник CODE, как правило, предпочтительнее для листингов кода.

1
2
>>> print PRE('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<pre id="0" class="test">&lt;hello&gt;<b>world</b></pre>

SCRIPT

SCRIPT

Включает или ссылается на скрипт, такой как JavaScript. Содержание между тегами отображается в виде HTML-комментария, в пользу действительно старых браузеров.

1
2
3
4
>>> print SCRIPT('alert("hello world");', _type='text/javascript')
<script type="text/javascript"><!--
alert("hello world");
//--></script>

SELECT

SELECT

Создает тег <select>...</select>. Это используется с OPTION помощником. АргументыSELECT, которые не являются объектами OPTION автоматически преобразуются в параметры.

1
2
3
4
5
>>> print SELECT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<select id="0" class="test">
   <option value="&lt;hello&gt;">&lt;hello&gt;</option>
   <option value="&lt;b&gt;world&lt;/b&gt;"><b>world</b></option>
</select>

SPAN

SPAN

Подобно DIV, но используется для обозначения содержания в строке (а не в блоке).

1
2
>>> print SPAN('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<span id="0" class="test">&lt;hello&gt;<b>world</b></span>

STYLE

STYLE

Похож на скрипт, но используется чтобы либо включить код CSS, либо сослаться на код CSS. Здесь CSS включается:

1
2
3
4
>>> print STYLE(XML('body {color: white}'))
<style><!--
body { color: white }
//--></style>

а здесь он ссылается:

1
2
3
>>> print STYLE(_src='style.css')
<style src="style.css"><!--
//--></style>

TABLE, TR, TD

TABLE
TR
TD

Эти теги (наряду с дополнительным THEAD, TBODY и TFOOTER помощниками) используются для создания HTML-таблиц.

1
2
>>> print TABLE(TR(TD('a'), TD('b')), TR(TD('c'), TD('d')))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TR ожидает TD содержимое; аргументы, которые не являются TD объектами, автоматически преобразуются.

1
2
>>> print TABLE(TR('a', 'b'), TR('c', 'd'))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Легко преобразовать массив Python в HTML-таблицу с помощью обозначения * перед аргументом функции Python, которое сопоставляет элементы в списке с позициями аргументов функции.

Здесь, мы преобразуем построчно:

1
2
3
>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(TR(*table[0]), TR(*table[1]))
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

Здесь мы преобразуем все строки сразу:

1
2
3
>>> table = [['a', 'b'], ['c', 'd']]
>>> print TABLE(*[TR(*rows) for rows in table])
<table><tr><td>a</td><td>b</td></tr><tr><td>c</td><td>d</td></tr></table>

TBODY

TBODY

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

1
2
>>> print TBODY(TR('<hello>'), _class='test', _id=0)
<tbody id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tbody>

TEXTAREA

TEXTAREA

Этот помощник делает тег <textarea>...</textarea>.

1
2
>>> print TEXTAREA('<hello>', XML('<b>world</b>'), _class='test')
<textarea class="test" cols="40" rows="10">&lt;hello&gt;<b>world</b></textarea>

Единственное ограничение в том, что его необязательный аргумент "value" перекрывает его содержимое (inner HTML)

1
2
>>> print TEXTAREA(value="<hello world>", _class="test")
<textarea class="test" cols="40" rows="10">&lt;hello world&gt;</textarea>

TFOOT

TFOOT

Используется для разметки строк нижнего колонтитула таблицы.

1
2
>>> print TFOOT(TR(TD('<hello>')), _class='test', _id=0)
<tfoot id="0" class="test"><tr><td>&lt;hello&gt;</td></tr></tfoot>

TH

TH

Используется вместо TD в заголовке таблицы.

1
2
>>> print TH('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<th id="0" class="test">&lt;hello&gt;<b>world</b></th>

THEAD

THEAD

Используется для разметки строк заголовка таблицы.

1
2
>>> print THEAD(TR(TH('<hello>')), _class='test', _id=0)
<thead id="0" class="test"><tr><th>&lt;hello&gt;</th></tr></thead>

TITLE

TITLE

Используется для разметки оглавления страницы в заголовке HTML.

1
2
>>> print TITLE('<hello>', XML('<b>world</b>'))
<title>&lt;hello&gt;<b>world</b></title>

TR

TR

Метки строк таблицы. Они должны отображаться внутри таблицы и содержать теги <td>...</td>. Аргументы TR, которые не являются TD объектами автоматически преобразуются.

1
2
>>> print TR('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tr id="0" class="test"><td>&lt;hello&gt;</td><td><b>world</b></td></tr>

TT

TT

Теги текста пишущей машинки (моноширинный).

1
2
>>> print TT('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<tt id="0" class="test">&lt;hello&gt;<b>world</b></tt>

UL

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

UL
1
2
>>> print UL('<hello>', XML('<b>world</b>'), _class='test', _id=0)
<ul id="0" class="test"><li>&lt;hello&gt;</li><li><b>world</b></li></ul>

URL

Помощник URL описан в главе 4 URL

embed64

embed64(filename=None, file=None, data=None, extension='image/gif') кодирует предоставленные (двоичные) данные в base64.

filename: если имеется, открывает и читает файл в режиме 'rb'. file: если имеется, читает этот файл. data: если имеется, использует указанные данные.

embed64

xmlescape

xmlescape(data, quote=True) возвращает экранированную строку из предоставленных данных.

xmlescape
1
2
>>> print xmlescape('<hello>')
&lt;hello&gt;

Пользовательские помощники

TAG

TAG

Иногда вам нужно создать пользовательские теги XML. web2py предоставляет TAG, который является универсальным генератором тегов.

1
{{=TAG.name('a', 'b', _c='d')}}

генерирует следующий XML

1
<name c="d">ab</name>

Аргументы "a", "b", и "d" автоматически экранируются; используйте XML помощник, чтобы подавить это поведение. Используя TAG вы можете сгенерировать HTML/XML теги, которые не предоставляются API. Теги могут быть вложенными и сериализованы через str(). Эквивалентный синтаксис:

1
{{=TAG['name']('a', 'b', c='d')}}

Если объект TAG создается с пустым именем, то он может быть использован для объединения вместе нескольких строк и HTML-помощников без вставки их в окружающий тег, но это использование не рекомендуется. Используйте вместо этого CAT помощника.

Самозакрывающиеся теги могут быть сгенерированы с помощником TAG. Имя тега должен заканчиваться символом "/".

1
{{=TAG['link/'](_href='http://web2py.com')}}

генерирует следующий XML:

1
<link ref="http://web2py.com"/>

Обратите внимание на то, что TAG является объектом, и TAG.name или TAG['name'] это функция, которая возвращает временный класс помощника.

MENU

MENU

Помощник MENU принимает список из списков или кортежей из формы response.menu (как описано в главе 4) и создает древовидную структуру с использованием неупорядоченных списков, представляющих меню. Например:

1
2
3
4
5
>>> print MENU([['One', False, 'link1'], ['Two', False, 'link2']])
<ul class="web2py-menu web2py-menu-vertical">
  <li><a href="link1">One</a></li>
  <li><a href="link2">Two</a></li>
</ul>
Первый элемент в каждом списке/кортеже является текстом, который будет отображаться для данного пункта меню. Второй элемент в каждом списке/кортеже является логическим значением, указывающим, активен ли этот конкретный пункт меню или нет (т.е. текущий выбранный элемент). При установке на True, то помощник MENU() добавит "web2py-menu-active" класс к <li> для этого элемента (вы можете изменить название этого класса через "li_active" аргумент MENU()). Другим способом указать активный URL является непосредственная передача его MENU() через аргумент "active_url". Третьим элементом в каждом списке/кортеже может быть HTML-помощник (который может включать в себя вложенные помощники), и помощник MENU будет просто оказывать ему помощь, а не создавать его собственный <a> тег.

Каждый элемент меню может иметь четвертый аргумент, которым является вложенное подменю (и так далее рекурсивно):

1
2
3
4
5
6
7
8
9
>>> print MENU([['One', False, 'link1', [['Two', False, 'link2']]]])
<ul class="web2py-menu web2py-menu-vertical">
  <li class="web2py-menu-expand">
     <a href="link1">One</a>
     <ul class="web2py-menu-vertical">
        <li><a href="link2">Two</a></li>
     </ul>
  </li>
</ul>

Элемент меню может также иметь дополнительный 5-ый элемент, который является логическим. Когда false, то пункт меню игнорируется помощником MENU.

Помощник MENU принимает следующие необязательные параметры:

  • _class: по умолчанию "web2py-menu web2py-menu-vertical" и устанавливает класс внешних элементов UL.
  • ul_class: по умолчанию "web2py-menu-vertical" и устанавливает класс внутренних элементов UL.
  • li_class: по умолчанию "web2py-menu-expand" и устанавливает класс внутренних элементов LI.
  • li_first: позволяет добавить класс к первому элементу списка.
  • li_last: позволяет добавить класс к последнему элементу списка.
mobile

MENU принимает необязательный аргумент mobile. При установке на True вместо построения рекурсивной структуры меню UL она возвращает выпадающий SELECT со всеми параметрами меню и атрибутом onchange, который перенаправляет на страницу, соответствующую выбранной опции. Это спроектировано для использования в качестве представления альтернативного меню, что увеличивает удобство использования на небольших мобильных устройствах, таких как телефоны.

Обычно меню используется в макете со следующим синтаксисом:

{{=MENU(response.menu, mobile=request.user_agent().is_mobile)}}

Таким образом, мобильное устройство распознается автоматически и меню визуализируется соответствующим образом.

BEAUTIFY

BEAUTIFY используется для создания HTML-представления составных объектов, в том числе списки, кортежи и словари:

1
{{=BEAUTIFY({"a": ["hello", XML("world")], "b": (1, 2)})}}

BEAUTIFY возвращает XML-подобный объект сериализованный в XML, с хорошим выглядящим представлением его аргумента конструктора. В этом случае XML-представление:

1
{"a": ["hello", XML("world")], "b": (1, 2)}

будет отображаться как:

1
2
3
4
<table>
<tr><td>a</td><td>:</td><td>hello<br />world</td></tr>
<tr><td>b</td><td>:</td><td>1<br />2</td></tr>
</table>

DOM и синтаксический анализ на стороне сервера

element
elements

Элементы elements

Помощник DIV и все производные помощники обеспечивают методы поиска element и elements.

element возвращает первый дочерний элемент, соответствующий заданному условию (или None, если нет соответствия).

elements возвращает список всех соответствующих детей (children).

element и elements используют тот же самый синтаксис, чтобы указать условие соответствия, что допускает три возможности, которые могут быть смешаны и подобраны: JQuery подобные выражения, совпадение по точному значению атрибута, совпадение с использованием регулярных выражений.

Вот простой пример:

1
2
3
4
5
>>> a = DIV(DIV(DIV('a', _id='target',_class='abc')))
>>> d = a.elements('div#target')
>>> d[0][0] = 'changed'
>>> print a
<div><div><div id="target" class="abc">changed</div></div></div>

Безымянный аргумент elements является строкой, которая может содержать: имя тега, id тега с предшествующим символом решетки, класс (class) с предшествующей точкой, явное значение атрибута в квадратных скобках.

Вот 4 эквивалентных способа поиска предыдущего тега по id:

1
2
3
4
>>> d = a.elements('#target')
>>> d = a.elements('div#target')
>>> d = a.elements('div[id=target]')
>>> d = a.elements('div',_id='target')

Вот 4 эквивалентных способа поиска предыдущего тега по class:

1
2
3
4
>>> d = a.elements('.abc')
>>> d = a.elements('div.abc')
>>> d = a.elements('div[class=abc]')
>>> d = a.elements('div',_class='abc')

Любой атрибут может использоваться, чтобы определить местонахождение элемента (не просто id и class), в том числе несколько атрибутов (функция element может принимать несколько именованных аргументов), но только будет возвращен первый соответствующий элемент.

Используя синтаксис jQuery "div#target" можно указать несколько критериев поиска, разделенных запятой:

1
2
>>> a = DIV(SPAN('a', _id='t1'), DIV('b', _class='c2'))
>>> d = a.elements('span#t1, div.c2')

или, что эквивалентно

1
2
>>> a = DIV(SPAN('a', _id='t1'), DIV('b', _class='c2'))
>>> d = a.elements('span#t1', 'div.c2')

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

1
2
>>> a = DIV(SPAN('a', _id='test123'), DIV('b', _class='c2'))
>>> d = a.elements('span', _id=re.compile('test\d{3}')

Специальным именованным аргументом DIV (и производных) помощников является find. Он может быть использован для указания значения поиска или регулярного выражения поиска в текстовом содержимом тега. Например:

1
2
3
4
>>> a = DIV(SPAN('abcde'), DIV('fghij'))
>>> d = a.elements(find='bcd')
>>> print d[0]
<span>abcde</span>

или

1
2
3
4
>>> a = DIV(SPAN('abcde'), DIV('fghij'))
>>> d = a.elements(find=re.compile('fg\w{3}'))
>>> print d[0]
<div>fghij</div>
components

Компоненты components

Вот пример листинга всех элементов в HTML строке:

1
2
html = TAG('<a>xxx</a><b>yyy</b>')
for item in html.components: print item

parent
sibling

Родитель и дети (parent и siblings)

parent возвращает родительский элемент текущего элемента.

1
2
3
4
5
6
7
8
>>> a = DIV(SPAN('a'),DIV('b'))
>>> s = a.element('span')
>>> d = s.parent
>>> d['_class']='abc'
>>> print a
<div class="abc"><span>a</span><div>b</div></div>
>>> for e in s.siblings(): print e
<div>b</div>

Замена элементов

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

1
2
3
4
>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=P('z')
>>> print a
<div><p>z</p><div><p>z</p></div>

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

1
2
3
4
>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=lambda t: P(t[0])
>>> print a
<div><p>x</p><div><p>y</p></div>

Если replace=None, совпадающие элементы будут полностью удалены.

1
2
3
4
>>> a = DIV(SPAN('x'), DIV(SPAN('y'))
>>> b = a.elements('span', replace=None)
>>> print a
<div></div>
flatten

Сплющивание flatten

Метод Flatten рекурсивно сериализует содержание детей данного элемента в обычный текст (без тегов):

1
2
3
>>> a = DIV(SPAN('this', DIV('is', B('a'))), SPAN('test'))
>>> print a.flatten()
thisisatest

Flatten может быть передан необязательный аргумент, render, т.е. функция, которая визуализирует/сплющивает содержание, используя другой протокол. Вот пример сериализации некоторых тегов в синтаксис Markmin вики:

1
2
3
4
5
6
>>> a = DIV(H1('title'), P('example of a ', A('link', _href='#test')))
>>> from gluon.html import markmin_serializer
>>> print a.flatten(render=markmin_serializer)
# titles

example of [[a link #test]]

На момент написания мы предоставляем markmin_serializer и markdown_serializer.

Разбор (Parsing)

Объект TAG является также XML/HTML парсером. Он может читать текст и конвертировать в древовидную структуру помощников. Это позволяет осуществлять манипуляции с использованием вышеуказанного API:

1
2
3
4
5
>>> html = '<h1>Title</h1><p>this is a <span>test</span></p>'
>>> parsed_html = TAG(html)
>>> parsed_html.element('span')[0]='TEST'
>>> print parsed_html
<h1>Title</h1><p>this is a <span>TEST</span></p>

page layout
layout.html
extent
include

Макет страницы

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

Например, мы можем придумать представление "index.html", которое расширяет "layout.html" и включает "body.html". В то же время, "layout.html" может включать "header.html" и "footer.html".

Корнем дерева является то, что мы называем макетом. Так же, как и любой другой файл шаблона HTML, вы можете редактировать его с помощью web2py интерфейса администратора. Имя файла "layout.html" это просто соглашение.

Вот минималистическая страница, которая расширяет "layout.html" макет и включает в себя "page.html" представление:

1
2
3
{{extend 'layout.html'}}
<h1>Hello World</h1>
{{include 'page.html'}}

Расширяемый файл макета должен содержать {{include}} директиву, что-то вроде:

1
2
3
4
5
6
7
8
<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    {{include}}
  </body>
</html>

Когда представление вызывается, то загружается расширяемое (макет) представление, и вызывающее представление заменяет {{include}} директиву внутри макета. Обработка продолжается рекурсивно до тех пор, пока все extend и include директивы не будут обработаны. Полученный шаблон затем преобразуется в код Python. Обратите внимание, когда приложение компилируется в байткод, то именно этот Python код будет скомпилирован, а не оригинальное представление самих файлов. Таким образом, скомпилированная в байткод версия данного представления является единственным .pyc файлом, который включает в себя код Python не только для исходного файла представления, но и для всех его деревьев расширенных и включенных представлений.

extend, include, block и super это специальные директивы шаблона, не команды Python.

Любое содержание или код, который предшествует {{extend ...}} директиве будет вставлен (и, следовательно, выполнен) перед началом содержимого/кода расширяемого представления. Несмотря на то, что это, как правило, не используется для вставки фактического содержания HTML перед содержимым расширяемого представления, он может быть полезен в качестве средства для определения переменных или функций, которые вы хотите сделать доступными для расширяемого представления. Например, рассмотрим представление "index.html":

1
2
3
{{sidebar_enabled=True}}
{{extend 'layout.html'}}
<h1>Home Page</h1>

и выдержку из "layout.html":

1
2
3
4
5
{{if sidebar_enabled:}}
    <div id="sidebar">
        Sidebar Content
    </div>
{{pass}}

Так как sidebar_enabled присвоению в "index.html" предшествует extend, эта строка вставляется перед началом "layout.html", делая sidebar_enabled доступным в любом месте в пределах "layout.html" кода (несколько более сложный вариант этого используется в welcome приложении).

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

Аргументомextend или include (т.е. имя расширенного или включенного представления) может быть переменная python (однако не выражение python). Тем не менее, это накладывает ограничение -- представлениям, которые используют переменные в extend или include операторы не могут быть скомпилированы в байткод. Как было отмечено выше, скомпилированные в байткод представления включают в себя все дерево расширенных и включенных представлений, поэтому конкретные расширяемые и включенные представления должны быть известны во время компиляции, что невозможно, если имена являются переменными представления (значения которых пока не определены до момента выполнения). Поскольку скомпилированные в байткод представления могут обеспечить значительный прирост скорости, то использование переменных в extend и include как правило, следует избегать, если это возможно.

В некоторых случаях альтернативой использованию переменной в include является простое размещение регулярных {{include ...}} директив внутри if...else блока.

1
2
3
4
5
{{if some_condition:}}
{{include 'this_view.html'}}
{{else:}}
{{include 'that_view.html'}}
{{pass}}

Приведенный выше код не представляет никакой проблемы для компиляции в байткод, поскольку переменные не участвуют. Однако следует отметить, что байткод скомпилированные представления на самом деле будет включать в себя код Python для обоих "this_view.html" и "that_view.html", хотя только код для одного из этих представлений будет выполняться, в зависимости от значения some_condition.

Имейте в виду, это работает только для include -- вы не можете поместить {{extend ...}} директивы внутри if...else блоков.

response.menu
menu
response.meta
meta

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

1
2
3
4
5
6
7
8
response.title
response.subtitle
response.meta.author
response.meta.keywords
response.meta.description
response.flash
response.menu
response.files

За исключением menu и files, все эти строки и их смысл должен быть очевиден.

Меню response.menu представляет собой список из 3-х кортежей или 4-х кортежей. Эти три элемента: название ссылки, логическое значение, представляющее активна ли эта ссылка (является текущей ссылкой) и URL связанной страницы. Например:

1
2
response.menu = [('Google', False, 'http://www.google.com',[]),
                 ('Index',  True,  URL('index'), [])]
sub-menu

Четвертый элемент кортежа является необязательным подменю.

response.files список CSS и JS файлов, которые необходимы вашей странице.

Мы также рекомендуем использовать:

1
{{include 'web2py_ajax.html'}}

в заголовке HTML, так как это включить библиотеки JQuery и определяет некоторую обратную совместимость функций JavaScript для специальных эффектов и Ajax. "web2py_ajax.html" включает в себя response.meta теги в представлении, jQuery база, календарь DatePicker, и все необходимые CSS и JS response.files.

Макет страницы по умолчанию

Twitter Bootstrap

Макет "views/layout.html", который поставляется со скаффолдинг-приложением web2py welcome (отрезаны некоторые дополнительные части) является довольно сложным, но оно имеет следующую структуру:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>{{=response.title or request.application}}</title>
  ...
  <script src="{{=URL('static','js/modernizr.custom.js')}}"></script>

  {{
  response.files.append(URL('static','css/web2py.css'))
  response.files.append(URL('static','css/bootstrap.min.css'))
  response.files.append(URL('static','css/bootstrap-responsive.min.css'))
  response.files.append(URL('static','css/web2py_bootstrap.css'))
  }}

  {{include 'web2py_ajax.html'}}

  {{
  # используя боковые панели необходимо знать, какую боковую панель вы хотите использовать
  left_sidebar_enabled = globals().get('left_sidebar_enabled',False)
  right_sidebar_enabled = globals().get('right_sidebar_enabled',False)
  middle_columns = {0:'span12',1:'span9',2:'span6'}[
    (left_sidebar_enabled and 1 or 0)+(right_sidebar_enabled and 1 or 0)]
  }}

  {{block head}}{{end}}
</head>

<body>
  <!-- Панель навигации ================================================== -->
  <div class="navbar navbar-inverse navbar-fixed-top">
    <div class="flash">{{=response.flash or ''}}</div>
    <div class="navbar-inner">
      <div class="container">
        {{=response.logo or ''}}
        <ul id="navbar" class="nav pull-right">
          {{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}
        </ul>
        <div class="nav-collapse">
          {{if response.menu:}}
          {{=MENU(response.menu)}}
          {{pass}}
        </div><!--/.nav-collapse -->
      </div>
    </div>
  </div><!--/top navbar -->

  <div class="container">
    <!-- топ мачта ================================================== -->
    <header class="mastheader row" id="header">
        <div class="span12">
            <div class="page-header">
                <h1>
                    {{=response.title or request.application}}
                    <small>{{=response.subtitle or ''}}</small>
                </h1>
            </div>
        </div>
    </header>

    <section id="main" class="main row">
        {{if left_sidebar_enabled:}}
        <div class="span3 left-sidebar">
            {{block left_sidebar}}
            <h3>Left Sidebar</h3>
            <p></p>
            {{end}}
        </div>
        {{pass}}

        <div class="{{=middle_columns}}">
            {{block center}}
            {{include}}
            {{end}}
        </div>

        {{if right_sidebar_enabled:}}
        <div class="span3">
            {{block right_sidebar}}
            <h3>Right Sidebar</h3>
            <p></p>
            {{end}}
        </div>
        {{pass}}
    </section><!--/main-->

    <!-- нижний колонтитул ================================================== -->
    <div class="row">
        <footer class="footer span12" id="footer">
            <div class="footer-content">
                {{block footer}} <!-- this is default footer -->
                ...
                {{end}}
            </div>
        </footer>
    </div>

  </div> <!-- /container -->

  <!-- The javascript =============================================
       (Placed at the end of the document so the pages load faster) -->
  <script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
  <script src="{{=URL('static','js/web2py_bootstrap.js')}}"></script>
  {{if response.google_analytics_id:}}
    <script src="{{=URL('static','js/analytics.js')}}"></script>
    <script type="text/javascript">
    analytics.initialize({
      'Google Analytics':{trackingId:'{{=response.google_analytics_id}}'}
    });</script>
  {{pass}}
</body>
</html>

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

  • Он написан на HTML5 и использует "modernizr" [modernizr] библиотеку для обеспечения обратной совместимости. Фактический макет включает в себя некоторые дополнительные условные операторы, необходимые для IE и они опущены для краткости.
  • Он показывает оба response.title и response.subtitle которые могут быть установлены в модели. Если они не установлены, то он принимает имя приложения как заголовок
  • Он включает в себя web2py_ajax.html файл в заголовке, который генерирует все операторы импорта ссылок и сценариев.
  • Он использует модифицированную версию Twitter Bootstrap для гибких макетов, которые работают на мобильных устройствах и повторно упорядочивают столбцы, чтобы соответствовать маленьким экранам.
  • Он использует "analytics.js" для подключения к Google Analytics.
  • {{=auth.navbar(...)}} отображает welcome текущему пользователю и ссылки на функции auth вроде вход, выход, регистрация, изменение пароля и т.д. в зависимости от контекста. Это помощник фабрики и его выходом можно манипулировать, как и любым другим помощником. Он расположен в {{try:}}...{{except:pass}} в случае, если auth не определено.
  • {{=MENU(response.menu)}} отображает структуру меню, как <ul>...</ul>.
  • {{include}} заменяется содержимое расширяемого представления, когда отображается страница.
  • По умолчанию он использует условно три столбца (левую и правую боковые панели можно отключить с помощью расширяемого представления)
  • Он использует следующие классы: header, main, footer
  • Он содержит следующие блоки: statusbar, left_sidebar, center, right_sidebar, footer.

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

1
2
3
4
5
6
7
8
{{left_sidebar_enable=True}}
{{extend 'layout.html'}}

Этот текст идет по центру

{{block left_sidebar}}
This text goes in sidebar
{{end}}

Настройка макета по умолчанию

CSS

Пользовательская настройка макета по умолчанию без редактирования является легкой, потому что welcome приложение основано на Twitter Bootstrap, которое хорошо документировано и поддерживает темы. В web2py четырех статических файла, которые имеют отношение к стилю:

  • "css/web2py.css" содержит web2py определенные стили
  • "css/bootstrap.min.css" содержит стиль CSS Twitter Bootstrap [bootstrap]
    Bootstrap
  • "css/web2py_bootstrap.css" содержит с переопределением некоторые стили Bootstrap для соответствия потребностям web2py.
  • "js/bootstrap.min.js" которая включает в себя библиотеки для эффектов, модальности, панелей меню.

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

1
2
3
4
5
6
7
8
9
<style>
body { background: url('images/background.png') repeat-x #3A3A3A; }
a { color: #349C01; }
.header h1 { color: #349C01; }
.header h2 { color: white; font-style: italic; font-size: 14px;}
.statusbar { background: #333333; border-bottom: 5px #349C01 solid; }
.statusbar a { color: white; }
.footer { border-top: 5px #349C01 solid; }
</style>

Конечно, вы можете также полностью заменить "layout.html" и "web2py.css" файлы на ваши собственные.

Мобильная разработка

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

Для того, чтобы разработка для настольных и мобильных устройств была проще, web2py включает в себя декоратор @mobilize. Этот декоратор применяется к действиям, которые должны иметь нормальное представление и мобильное представление. Это демонстрируется здесь:

from gluon.contrib.user_agent_parser import mobilize
@mobilize
def index():
   return dict()

Обратите внимание на то, что декоратор должен быть импортирован перед использованием его в контроллере. Когда функция "index" вызывается из обычного браузера (настольный компьютер), web2py визуализирует возвращаемый словарь, используя представление "[controller]/index.html". Однако, когда он вызывается мобильным устройством, словарь визуализируется через "[controller]/index.mobile.html". Обратите внимание на то, что мобильные представления имеют расширение "mobile.html".

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

if request.user_agent().is_mobile:
    response.view.replace('.html','.mobile.html')

Задача создания "*.mobile.html" представлений остается за разработчиком, но мы настоятельно рекомендуем использовать "JQuery Mobile" плагин, который делает эту задачу очень легкой.

Функции в представлениях

Рассмотрим этот макет "layout.html":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{if 'mysidebar' in globals():}}{{mysidebar()}}{{else:}}
        my default sidebar
      {{pass}}
    </div>
  </body>
</html>

и это расширяющее представление

1
2
3
4
5
{{def mysidebar():}}
my new sidebar!!!
{{return}}
{{extend 'layout.html'}}
Hello World!!!

Обратите внимание, функция определена перед {{extend...}} оператором -- это приводит к созданию функции перед тем как код "layout.html" будет выполнен, так что функция может быть вызвана в любом месте в пределах "layout.html", и даже до оператора {{include}}. Также обратите внимание, функции включена в расширяющее представление без = префикса.

Код генерирует следующий вывод:

1
2
3
4
5
6
7
8
<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my new sidebar!!!
    </div>
  </body>
</html>

Обратите внимание на то, что функция определена в HTML (хотя он также может содержать код Python) так что response.write используется для записи его содержимого (функции не возвращает содержимое). Именно поэтому макет вызывает функцию представления используя {{mysidebar()}} раньше, чем {{=mysidebar()}}. Функции, определенные таким образом, могут принимать аргументы.

block

Блоки в представлениях

Основной способ сделать представление более модульным является использование {{block...}}s, и этот механизм является альтернативой механизму, рассмотренному в предыдущем разделе.

Для того, чтобы понять, как это работает, рассмотрим приложения на основе скаффолдинг-приложения Welcome, которое имеет макет layout.html. Этот макет включается в представление default/index.html через {{extend 'layout.html'}}. В макете layout.html предварительно заданы определенные блоки с определенным содержанием по умолчанию, и они, таким образом, включаются в default/index.html.

Вы можете переопределить содержимое этих блоков по умолчанию, заключив свой новый контент внутри блока с тем же именем. Расположение блока внутри layout.html не изменяется, но изменяется содержимое.

Вот упрощенная версия. Представьте себе, что это "layout.html":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{block mysidebar}}
        my default sidebar (this content to be replaced)
      {{end}}
    </div>
  </body>
</html>

и он является простым расширением представления default/index.html:

1
2
3
4
5
{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
my new sidebar!!!
{{end}}

Он генерирует следующий вывод, в котором содержание обеспечено с помощью верховного блока в расширяемом представлении, но заключенного в DIV и класс происходит от layout.html. Это обеспечивает согласованность между представлениями:

1
2
3
4
5
6
7
8
<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my new sidebar!!!
    </div>
  </body>
</html>

Реальный layout.html определяет ряд полезных блоков, и вы можете легко добавить больше, чтобы макет соответствовал вашим желаниям.

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

super

Внутри блока, вы можете использовать выражение {{super}}, чтобы включить содержимое родителя. Например, если мы заменим вышеуказанное расширяемое представление:

1
2
3
4
5
6
{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
{{super}}
my new sidebar!!!
{{end}}

мы получаем:

1
2
3
4
5
6
7
8
9
<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my default sidebar
        my new sidebar!!!
    </div>
  </body>
</html>
 top