Chapter 5: The views

Pohledy (views)

views
template language
HTML

Web2py používá jako jazyk pro své modely, kontroléry, a také pro pohledy, ačkoli syntaxe Pythonu v pohledech je lehce modifikovaná, aby poskytla lépe čitelný kód a zároveň nebylo třeba zavádět nějaká omezení ve srovnání s úplnými možnostmi jazyka Python.

Účelem pohledů je spojovat vložený kód (Python) a HTML dokument tak, aby kód Pythonu sestavil výsledný HTML dokument. Obecně vzato, vyvolává to 2 problémy:

  • Jak má být vložený Python kód uvozen (odlišen)?
  • Má se odsazování řídit pravidly Pythonu nebo HTML dokumentu?

Web2py používá {{ ... }} pro označení kódu Pythonu, který je vložen do HTML. Použití složených závorek místo lomených má výhodu, že je to transparentní pro všechny běžné HTML editory. To pomáhá Web2py vývojáři kterýkoli z těchto editorů používat i pro tvorbu Web2py pohledů.

Protože vývojář vkládá spíše kód Pythonu do HTML, dokument by měl být odsazován podle HTML pravidel místo podle pravidel Pythonu. Z tohoto důvodu dovolujeme mezi značkami {{ ... }} používat neodsazovaný Python. Protože odsazování je pro Python zásadní pro rozpoznání ukončení bloků kódu, musíme zavést jiný způsob, jak bloky kódu srozumitelně ukončit. To je důvodem, proč Web2py speciálním způsobem využívá klkíčové slovo pass Pythonu.

Blok kódu začíná řádkem, který je zakončený dvojtečkou, kdežto končí řádkem, na němž je napsáno pass (nebo který začíná pass). Klíčové slovo pass není nutné, když ukončení bloku je zřejmé z kontextu. Pozn.překladatele: Pozor, uvedení pass tam, kde je ukončení bloku jasné, může způsobit i chybu. Jde např. o uvedení před příkazem else: nebo za příkazem break. Neboli pass použijte opravdu jen tam, kde konec bloku označuje pro Python jen návrat odsazení - pokud je někde pass vyžadován a neuvedli jste ho, rychle takovou chybu najdete na základě chybového hlášení missing 'pass' in view.

Tady je příklad:

{{
if i == 0:
response.write('i is 0')
else:
response.write('i is not 0')
pass
}}

Poznamenejme, že pass je klíčové slovo Pythonu, nikoli Web2py. Některé editory pro Python, například Emacs, ppoužívají slovo pass pro označení rozdělení bloků a pro automatické přeformátování odsazení kódu.

Web2py template jazyk (jazyk pro formátování pohledů) dělá přesně totéž. Kdykoli najde něco jako:

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

přeloží to takto:

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.

Když je ve Web2py pohledu chyba, chybové hlášení pak reportuje přeložený (generovaný) kód pohledu místo originálního kódu, jak jej vývojář zapsal v editoru. To pomůže vývojáři při ladění tím, že může být zvýrazněna /highlight/ syntaxe skutečně prováděného kódu (a také je možné si při ladění pomoci HTML editorem nebo DOM inspectorem prohlížeče).

Důležité je se ještě seznámit s významem úvodního rovnítka:

{{=x}}

Tím generujeme:

response.write
escape

response.write(x)

Výrazy, takto vložené do HTML, jsou defaultně escapovány (naharazeny nepovolené znaky HTML obsahu značek). Escapování je ale potlačeno, jestliže na místě x je vkládán XML objekt ?/, even if escape is set to True/.

Tady je příklad, v němž se objevuje H1 helper:

{{=H1(i)}}

a to se přeloží jako:

response.write(H1(i))

Po vyhodnocení jsou H1 objekt a jeho komponenty rekurzivně serializovány, escapovány a tak zapsány do těla odpovědi /into response body/. Značky (tagy), které generuje H1 a vnitřní HTML kód zůstávají beze změny - nejsou escapovány. Tento mechanismus zaručuje, že veškerý text --- a právě pouze text ---, zobrazený ve stránce, je vždy escapován, a tím je bráněno XSS (cross site scripting) zranitelnosti. Současně kód zůstává jednoduchý a snadný pro ladění.

Metoda response.write(obj, escape=True) má 2 argumenty, objekt, který se má zapsat do výstupu, a zda má být escapovány (True defaultně). Jestliže obj.xml() metodu, zavolá se tato metoda a její výsledek se přepíše do výstupu (escape parametr je v tomto případě ignorován). V opačném případě se k serializaci použije __str__ metoda objektu a escape parametr se uplatní: pokud je True, znaky budou nahrazeny. Všechny vestavěné helpery (H1 v uvedeném příkladu) jsou objekty, které vědí, jak se mají serializovat (neboli mají definovanou .xml() metodu).

Toto se děje automaticky. Nikdy nepotřebujete (a ani byste neměli) volat metodu response.write explicitně.

Základní syntaxe

Ve Web2py jazyce pro šablony tedy máte k dispozici všechny řídící struktury Pythonu. Zde uvádíme některé příklady použití při formátování výstupu. Samozřejmě je možné tyto struktury také vnořovat.

for...in

for

V pohledech můžete ve smyčce procházet jakýkoli iterovatelný objekt:

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

čímž získáte nabídku:

<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>

items může být jakýkoli iterovatelný objekt, jako je seznam (list), vektor (tuple), nebo Web2py Rows objekt (umožňující vypsat v cyklu řádky tabulky), případně kterýkoli objekt, designovaný jako iterátor. Jednotlivé prvky jsou nejprve serializovány a escapovány.

while

while

Smyčku můžete vytvořit také pomocí klíčového slova while:

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

čímž dostanete:

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

if...elif...else

if
elif
else

Můžete výstup podmíněně větvit:

{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 2:}}je liché{{else:}}je sudé{{pass}}
</h2>

čímž získáte např.:

<h2>
45 je liché
</h2>

Protože je zřejmé, že else ukončuje první blok příkazů v příkazu if, tuto první část příkazem pass neukončujeme - případné použití pass by mohlo způsobit chybu. Ale blok za else nebo příkaz if bez else musíte pomocí pass ukončit.

Připomeňme si, že v Pythonu "else if" se píše jako elif jako v tomto příkladu:

{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 4 == 0:}}je dělitelné 4
{{elif k % 2 == 0:}}je sudé
{{else:}}je liché
{{pass}}
</h2>

takže získáte např.:

<h2>
64 je dělitelné 4
</h2>

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

try
except
else
finally

V pohledech je možné používat i příkaz try...except s následujícím očekávatelným, ale přece jen pozornost zasluhujícím chováním. Uvažujme následující příklad:

{{try:}}
Zkouška {{= 1 / 0}}
{{except:}}
dělení nulou
{{else:}}
nedošlo k dělení nulou
{{finally}}
<br />
{{pass}}

Získáme následující výstup:

Zkouška
dělení nulou
<br />

Tento příklad ilustruje, že veškerý výstup před tím, než nastane výjimka, je odeslán do výstupu (a to včetně výstupu, kdy z hlediska Python kódu nemáme nic mezi příkazem 'try:' a příkazem, který vyvolá výjimku). Text "Zkouška" se tedy do výstupu odešle, protože se nachází před vyvoláním výjimky.

def...return

def
return

Web2py šablonovací jazyk umožňuje definovat a implementovat funkce, které mohou vracet objekty nebo zapisovat text/html řetězce do výstupu. Ukažme si 2 příklady:

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

vytvoří tento výstup:

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

Funkce itemize1 vrátí helper objekt, který bude vložen na místo, kde funkci zavoláme.

Předpokládejme nyní následující kód:

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

Získáme přesně stejný výstup jako v předchozím příkladu. V tomto případě funkce itemize2 představuje kus HTML, který vyplní tag, ze kterého je volán. Všimněte si, že není uvedeno '=' před voláním itemize2, protože funkce text nevrací (vrací None, což by se také ještě navíc vypsalo v případě použití rovnítka), místo toho text přípo zapisuje do výstupu /do response/.

Je tu jedna anomálie: funkce, definované uvnitř pohledu, musí povinně končit příkazem 'return' (jinak selže odsazování / ukončování bloků příkazů).

HTML helpery

helpers

Předpokládejme, že v pohledu je následující kód:

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

Výstup bude vypadat takto:

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

DIV je helper třída, tedy něco, co lze použít k programovému sestavování HTML. Odpovídá <div> značce HTML.

Poziční argumenty se interpretují jako objekty, obsažené mezi otevírací a zavírací značkou (tagem). Pojmenované argumenty, které začínají podtržítkem, jsou interpretovány jako stejnojmenné atributy HTML tagu (ale bez úvodního podtržítka). Některé helpery také mají pojmenované parametry, jejichž jméno podtržítkem nezačíná - ty pak způsobují něco specifického pro ten který helper.

Místo sady nepojmenovaných argumentů také můžete použít seznam (list) nebo vektor (tuple) a využít * notace pro rozvoj seznamu. Podobně místo pojmenovaných argumentů můžete předat slovník a využít notace **. Například:

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

(získáme stejný výstup jako předtím).

Následující sada helperů:

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

může být použita pro sestavení složitých výrazů, které pak budou serializovány do XML.[xml-w] [xml-o]. Například:

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

vytvoří výstup:

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

Helpery také mohou být explicitně serializovány do řetězců, shodně buď pomocí __str__ nebo xml metody:

>>> print str(DIV("hello world"))
<div>hello world</div>
>>> print DIV("hello world").xml()
<div>hello world</div>
Document Object Model (DOM)

Mechanismus helperů ve Web2py je víc než systém pro generování HTML bez spojování řetězců. Poskytuje reprezentaci Document Object Modelu (DOM) na straně serveru.

Na komponenty helperů se můžeme odkazovat pomocí jejich pozice, a helpery tedy fungují jako seznamy jejich komponent:

>>> 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>

Na atributy helperů můžeme odkazovat pomocí jejich jména, a helpery tedy fungují jako slovníky (dictionaries) ve vztahu k atributům:

>>> 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>

Celá sada komponent je přístupná jako seznam (list) a.components a celá sada atributů je přístupná jako slovník (dictionary) a.attributes. a[i] je ekvivalent pro a.components[i], jestliže i je integer, kdežto a[s] je ekvivalent pro a.attributes[s], když s je řetězec.

Atributy se předávají helperu jako pojmenované atributy /keyword arguments/ to the helper. Ale v některých případech jména atributů obsahují speciální znaky, které nejsou povoleny v identifikátorech Pythonu (např. pomlčky) a proto nemohou být předány jako keyword argument. Například:

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

nebude pracovat, protože "_data-role" obsahuje pomlčku, která způsobí chybu syntaxe Pythonu.

V takových případech můžete místo toho předat atributy jako slovník (dictionary) v kombinaci s ** notací:

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

Můžete také dynamicky tvořit speciální TAGy:

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

XML

XML

XML je objekt, použitý pro uvození textu, který nemá být escapován. Není důležité, zda obsahem je skutečně platné XML nebo ne. Obsahem např. může být JavaScript.

Text zde bude escapován:

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

a použitím XML můžete zabránit escapování:

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

Někdy můžete potřebovat renderovat HTML, uložené v proměnné, ale HTML může obsahovat nikoli bezpečné tagy, např. skripty:

>>> print XML('<script>alert("nebezpečí!")</script>')
<script>alert("nebezpečí!")</script>

Neescapovaný vstup, který je schopen vykonání, jako je tomu v tomto příkladu (například zadaný do těla kmentáře v blogu) není bezpečný, protože může být použit ke Cross Site Scripting (XSS) útokům proti ostatním návštěvníkům stránky.

sanitize

Web2py XML helper může náš text ošetřit, aby zabránil napadení a escapovat všechny značky, kromě těch, které explicitně povolíte. Tady je příklad:

>>> print XML('<script>alert("nebezpečí!")</script>', sanitize=True)
&lt;script&gt;alert(&quot;nebezpečí!&quot;)&lt;/script&gt;

XML konstruktor defaultně považuje některé tagy a některé jejich atributy za bezpečné. Můžete přepsat defaultní nastavení pomocí volitelných permitted_tags a allowed_attributes argumentů, přičemž zde vypsané platí právě pro defaultní nastavení XML helperu:

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']})

Pozn.překladatele: Asi má být sanitize=True

Vestavěné helpery

A

Tento helper sestavuje odkazy (linky).

A
>>> 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>

Místo _href můžete zadat URL pomocí callback argumentu. Například v pohledu:

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

a efektem stisknutí odkazu bude ajax volání "myaction" místo přesměrování na jinou adresu. V tomto případě můžete uvést 2 volitelné argumenty navíc: target a delete:

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

Odpověď ajax callbacku bude uložena do DIVu s id="t".

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

Po odpovědi bude nejbližší tag /the closest tag matching/ "div#b" odstraněn. V tomto případě bude smazáno tlačítko.

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

Stisknutí takového tlačítka způsobí callback a zrušení aktuálního řádku.

callback adelete mohou být kombinovány.

helper A má speciální argument cid. Pracuje takto:

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

Kliknutí na odkaz rovněž způsobí natažení obsahu do cílového <div>. Je to podobné jako syntaxe nahoře, ale jde o výkonnější verzi, která je speciálně vytvořena pro obnovování obsahu komponent. Toto využití parametru cid popisujeme podrobněji v kapitole 12 v oddílu o komponentách.

Tyto ajax vlastnosti vyžadují jQuery a "static/js/web2py_ajax.js", spolupráci umožňuje umístění příkazu {{include 'web2py_ajax.html'}} do pohledu (např. do hlavičky layout.html). "views/web2py_ajax.html" definuje některé proměnné a připojí všechny potřebné js a css soubory.

B
B

Tento helper změní obsah na bold (na tučné písmo).

>>> 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

Tento helper sestaví tělo stránky.

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

Tento helper vloží odřádkování (line break).

>>> print BR()
<br />
CAT (1.98.1 and up)
CAT

Spojuje ostatní helpery, je to totéž jako TAG[].

>>> print CAT('Tady je ', A('odkaz',_href=URL()), ' a nějaký ', B('text tučně'), '.')
Tady je <a href="/app/default/index">odkaz</a> a nějaký <b>text tučně</b>.
CENTER
CENTER

Tento helper centruje svůj obsah.

>>> 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

Tento helper zajišťuje zvýraznění syntaxe pro kód Pythonu, C, C++, HTML a Web2py a je výhodnější než PRE pro ukázky kódu. CODE má také schopnost tvořit odkazy do dokumentace Web2py API.

Tady je příklad zvýraznění příkazů v sekci s kódem Pythonu.

>>> 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>

Podobný příklad pro HTML

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

Zde jsou vypsány defaultní parametry helperu CODE:

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

Podporované hodnoty pro language parametr jsou "python", "html_plain", "c", "cpp", "web2py" a "html". "html" interpretuje {{ a }} značky jako "web2py" kód - na rozdíl od "html_plain".

Je-li uvedena hodnota link, např. "/examples/global/vars/", Web2py API odkazy v kódu vytvoří odkazy na dokumentaci na uvedené URL. Například "request" bude odkazován na "/examples/global/vars/request". V tomto případě odkaz zpracuje akce "vars" kontroléru "global.py", který je distribuován s Web2py jako součást aplikace "examples".

counter řídí číslování řádků. Lze uvést None pro potlačení číslování, číselnou hodnotu pro nastavení počátečního čísla řádku, nebo řetězec. V posledním případě číslování chybí a řetězec se interpretuje jako výzva (prompt) před příkazy.

styles argument: Když se podíváte na výše generované HTML, vidíte dva sloupce (číslování a kód), každý z nich má inline (přímo do HTML) uvedený styl. styles atributem tyto 2 defaultní styly přepíšete. Například:

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

styles atribut musí být slovník (dictionary), který může mít 2 klíče: CODE pro styl kódu, a LINENUMBERS pro styl levého sloupce s číslování. Nové styly zcela přepíší defaultní styly (nejsou k nim tedy jen přidány).

COL
COL
>>> print COL('a','b')
<col>ab</col>
COLGROUP
COLGROUP
>>> print COLGROUP('a','b')
<colgroup>ab</colgroup>
DIV

Pro formátování bloku. Ze třídy helperu DIV jsou odvozeny všechny ostatní helpery (kromě XML) a dědí jeho základní metody.

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

/Emphasize/

EM
>>> 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

Slouží pro vytvoření INPUT pole společně s jeho labelem (popisným textem).

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

Toto je jeden z nejdůležitějších helperů. V jeho jednoduché podobě jen vytvoří <form>...</form> tag, ale protože helpery jsou objekty a mají povědomost o tom, co obsahují, může zpracovávat potvrzený (submitted) formulář (například provádět validací polí). To popisujeme v kapitole 7.

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

"enctype" je defaultně "multipart/form-data".

hidden

Konstruktor pro FORM (a pro SQLFORM) může také dostat speciální argument hidden. Je-li slovník (dictionary) uveden v parametru hidden, jeho prvky jsou přidány jako skrytá (hidden) pole INPUT. Například:

>>> 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

Helpery pro titulky (hlavičky) odstavců:

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

Pro sestavení HEAD tagu HTML stránky.

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

HTML
XHTML

Tento helper je poněkud odlišný. Mimo vytvoření <html> tagu jej doplní doctype řetězcem[xhtml-w,xhtml-o,xhtml-school] .

>>> 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 helper má také některé nepovinné parametry s následujícími defaultními hodnotami:

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

kde doctype může být 'strict', 'transitional', 'frameset', 'html5' nebo kompletně zadaný doctype řetězec.

XHTML
XHTML

XHTML je podobné jako HTML, ale vytvoří XHTML doctype.

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

kde doctype může být 'strict', 'transitional', 'frameset' nebo kompletně zadaný doctype řetězec.

HR
HR

Vytvoří vodorovnou čáru.

>>> print HR()
<hr />
I
I

Šikmé (italic) písmo.

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

Vytvoří <input.../> tag. Input značka nemůže obsahovat jiné značky a je ukončena /> místo >. Helper má nepovinný parametr _type, který lze nastavit jako "text" (default), "submit", "checkbox" nebo "radio".

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

Má také nepovinný argument "value", který se liší od "_value". Zatímco posledně uvedený nastavuje defaultní hodnotu input pole, první (bez podtržítka) nastavuje aktuální hodnotu. Pro input typu "text" první varianta přepisuje druhou:

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

V případě radio buttonu, INPUT nastaví "checked" atribut:

radio
>>> 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

a podobně pro checkbox:

checkbox
>>> 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" />
IFRAME

Tento helper vkládá jinou webovou stránku do aktuální. Url vložené stránky zadáme atributem "_src".

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

Vkládá obrázek do HTML:

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

Zde je příklad kombinace A, IMG a URL helperů pro vytvoření ikony jako odkazu:

>>> A(IMG(_src=URL('static','logo.png'), _alt="Moje Logo"),
      _href=URL('default','index'))
<a href="/myapp/default/index">
  <img src="/myapp/static/logo.png" alt="Moje Logo" />
</a>
LABEL

Pro vytvoření LABEL tagu polí INPUT.

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

Vytvoří legend tag formuláře.

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

Vytvoří položku seznamu uvnitř UL nebo OL tagu.

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

Pro sestavení META tagů v HTML hlavičce. Například:

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

Implementuje markmin wiki syntaxi. Konvertuje vstupní text na výstupní html podle markmin pravidel, jak je ukazuje příklad níže:

MARKMIN
>>> print MARKMIN("tohle je **tučně** nebo ''šikmo'' a tohle je [[odkaz http://web2py.com]]")
<p>tohle je <b>tučně</b> nebo <i>šikmo</i> a tohle je <a href="http://web2py.com">odkaz</a></p>

Markmin syntaxe je podrobně popsána v souboru, který je součástí distribuce Web2py:

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

a některé příklady najdete v kapitole 12 v oddíle o plugin_wiki, který MARKMIN více používá.

Markmin můžete použít ke generování HTML, LaTeX a PDF dokumentů:

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) # vyžaduje pdflatex

(MARKMIN helper je zkratkou pro markmin2html)

Hlavní pravidla syntaxe:

ZDROJVÝSTUP
# titulektitulek
## sekcesekce
### subsekcesubsekce
**tučně**tučně
''šikmo''šikmo''
``verbatim``verbatim
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>
[[klikni #mojekotva]]click me
$$\int_a^b sin(x)dx$$

Zařazení odkazu na obrázek, video nebo audio soubor bez dalšího označení automaticky vloží obrázek, video nebo audio (pro audio a video se použijí html <audio> a <video> značky).

Zařazení odkazu s qr: prefixem, například

qr:http://web2py.com

způsobí vložení odpovídajícího QR kódu s odkazem na uvedenou URL.

Zařazení odkazu s embed: prefixem, například

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

způsobí vložení obsahu stránky, např. v tomto případě youtube videa.

Obrázky lze také vkládat touto syntaxí:

[[image-description http://.../image.png right 200px]]

Nečíslovaný seznam:

- one
- two
- three

Číslovaný seznam:

+ one
+ two
+ three

Tabulky:

----------
 X | 0 | 0
 0 | X | 0
 0 | 0 | 1
----------

MARKMIN syntaxe podporuje blockquote, HTML5 audio a video tagy, určování umístění obrázků, uživatelské css, a tuto syntaxi lze rozšiřovat:

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

vygeneruje

'cbcb'

Uživatelské (Custom) bloky jsou odděleny pomocí ``...``:<key> a renderují se (zapíší do výstupu) pomocí funkce, předané takto: do parametru extra helperu předáme slovník, kde pro klíč (který byl použit ve vstupu markmin syntaxe) jako hodnotu uvedeme požadovanou převodní funkci. Pamatujte, že funkce může potřebovat escapovat svůj výstup, aby se zabránilo nebezpečí XSS (cross side scripting).

OBJECT

Pro vložení objektu (například flash playeru) do HTML.

OBJECT
>>> 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

Pro číslovaný seznam. Seznam by měl obsahovat LI tagy. Argumenty OL, které nejsou LI objekty budou automaticky rozšířeny o <li>...</li> značky.

OL
>>> 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

Kvůli zpětné kompatibilitě, jako alias pro True pro design checkboxů. Doporučuje se přímo používat True.

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

Dovoluje seskupit více voleb v SELECTu a je praktické pro úpravy vzhledu pomocí CSS.

OPTGROUP
>>> 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

Slouží jako položka kombinace SELECT/OPTION.

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

Stejně jako pro INPUT, Web2py rozlišuje mezi "_value" (value pro OPTION), a "value" (aktuální hodnota SELECTu). Položka, u níž se obě tyto hodnoty shodují, bude vybrána (selected).

selected
>>> print SELECT('a', 'b', value='b'):
<select>
<option value="a">a</option>
<option value="b" selected="selected">b</option>
</select>
P
P

Pro vyznačení odstavce.

>>> 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

Generuje <pre>...</pre> tag pro zobrazení předem formátovaného textu. CODE helper je obecně užitečnější pro zobrazení výpisu a příkladů kódu.

>>> 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

Pro vložení skriptu, typicky JavaScriptu, nebo odkaz na něj. Obsah mezi značkami je do HTML výstupu poslán jako komentář, s ohledem na prehistorické prohlížeče.

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

Vytvoří tag <select>...</select>, jehož položky lze plnit OPTION helperem. Argumenty SELECTu, které nejsou zadány jako OPTION objekty, budou na položky (options) automaticky zkonvertovány.

>>> 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

Pro formátování bloku uvnitř řádky (inline).

>>> 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

Podobné jako SCRIPT, ale pro vložení CSS kódu nebo odkázání na něj. Jako příklad vložení stylu:

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

a odkaz na styl:

>>> print STYLE(_src='style.css')
<style src="style.css"><!--
//--></style>
TABLE, TR, TD

TABLE
TR
TD

Tyto helpery (spolu s volitelnými THEAD, TBODY a TFOOTER helpery) slouží pro vytvoření HTML tabulek.

>>> 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 očekává TD obsah; argumenty, které nejsou TD objekty, budou zkonvertovány automaticky.

>>> 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>

Seznam nebo pole (array) Pythonu je snadné převést na HTML tabulku pomocí * notace pro argumenty funkcí, což převádí prvky seznamu na poziční argumenty funkce.

Tady je příklad, kde postupujeme po jednotlivých řádcích:

>>> 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>

A tady to uděláme najednou:

>>> 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

Používá se pro vyznačení řádků v těle tabulky, na rozdíl od řádků hlavičky nebo patičky. Nemusí být použito.

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

Vytvoří <textarea>...</textarea> tag (pro zadání dlouhých textů ve formuláři).

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

Nepovinný argument "value" přepíše obsah tagu (inner HTML)

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

Používá se pro označení řádků v patičce tabulky.

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

Používá se místo TD v hlavičce tabulky.

>>> 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

Používá se pro označení řádků v hlavičce tabulky.

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

Pro označení titulku stránky v HTML hlavičce.

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

Označuje řádek tabulky. Lze používat uvnitř tabulky a vkládat <td>...</td> tagy. TR argumenty, které nejsou TD objekty, budou automaticky zkonvertovány.

>>> 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

Označuje text jako typewriter (monospaced; neproporcionální) text.

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

Vytváří nečíslovaný seznam pro vkládání LI položek. Není-li obsah označen jako LI, UL helper to udělá automaticky.

UL
>>> 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>
embed64

embed64(filename=None, file=None, data=None, extension='image/gif') zakóduje binární data do base64.

filename: jestliže je uvedeno, otevře soubor a přečte z něj obsah v 'rb' módu. file: jestliže je uvedeno, načte data z otevřeného souboru. data: jestliže je uvedeno, použije tento přímo zadaný obsah.

embed64
xmlescape

xmlescape(data, quote=True) vrátí escapovaný řetězec ze zadaného vstupního řetězce.

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

Uživatelské helpery

TAG
TAG

Někdy potřebujete generovat uživatelské XML tagy. Web2py má k dispozici helper TAG jako univerzální generátor značek.

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

sestaví náaledující XML:

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

Argumenty "a", "b" a "d" budou automaticky escapovány; toto chování potlačíte použitím XML helperu. Pomocí TAG můžete sestavovat jakékoli HTML/XML tagy. Mohou být vnořovány a serializují se pomocí str(). Ekvivalentní syntaxe je:

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

Jestliže je TAG objekt vytvořen bez zadání jména tagu, může být použit ke spojování více řetězců a HTML helperů, aniž by byly vloženy do vše obalující značky. Ale toto použití TAG považujeme za zastaralé (deprecated) - pro tento účel dejte přednost CAT helperu.

Poznamenejme, že TAG je objekt, a TAG.name nebo TAG['name'] je funkce, která vrací dočasnou helperovou třídu. ?/TAG is an object, and TAG.name or TAG['name'] is a function that returns a temporary helper class./

MENU
MENU

MENU helper vezme seznam (list) seznamů nebo vektorů (tuples) ve formátu response.menu (jak popisuje kapitola 4) a vygeneruje "stromovou (tree-like)" strukturu s nečíslovanými seznami pro vytvoření menu. Například:

>>> print MENU([['Jedna', False, 'odkaz1'], ['Dvě', False, 'odkaz2']])
<ul class="web2py-menu web2py-menu-vertical">
  <li><a href="odkaz1">Jedna</a></li>
  <li><a href="odkaz2">Dvě</a></li>
</ul>

Každá položka může mít čtvrtý argument, kterým je vnořené submenu (a tak dále, rekurzivně):

>>> print MENU([['Jedna', False, 'odkaz1', [['Dvě', False, 'odkaz2']]]])
<ul class="web2py-menu web2py-menu-vertical">
  <li class="web2py-menu-expand">
     <a href="odkaz1">Jedna</a>
     <ul class="web2py-menu-vertical">
        <li><a href="odkaz2">Dvě</a></li>
     </ul>
  </li>
</ul>

Položka menu může mít ještě nepovinný pátý parametr, boolean. Jestliže je false, MENU helper bude tuto položku ignorovat.

MENU helper může mít tyto argumenty:

  • _class: default je "web2py-menu web2py-menu-vertical", nastavuje třídu vnějšího UL prvku.
  • ul_class: default je "web2py-menu-vertical", nastavuje třídu vnitřních UL prvků.
  • li_class: default je "web2py-menu-expand", nastavuje třídu vnitřních LI prvků.
mobile

MENU má nepovinný argument mobile. Je-li nastaven na True, místo aby byla sestavena rekurzivní UL struktura, bude vrácen SELECT dropdown s položkami stejné úrovně a onchange atributem, který přesměruje na stránku, která odpovídá vybrané volbě. Tato verze je designována jako alternativní reprezentace menu, která zlepší použitelnost na malých mobilních zařízeních, např. telefonech.

Normálně je menu v layout.html voláno s touto syntaxí:

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

Tím je mobilní zařízení automaticky detekováno a menu je renderováno vhodným způsobem pro konkrétní zařízení.

BEAUTIFY

BEAUTIFY je vhodné pro sestavení HTML reprezentace složených objektů, jako jsou seznamy (lists), vektory (tuples), nebo slovníky (dictionaries):

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

BEAUTIFY vrátí XML-like objekt, serializovatelný do XML, s přehlednou reprezentací argumentu svého konstruktoru. Např. tento vstup:

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

se bude serializovat takto:

<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>

Document-object-model (DOM) na straně serveru a parsování

element
elements

elements

DIV helper a všechny z něj děděné helpery poskytují metody element a elements.

element vrátí první child (vnořený) prvek, který odpovídá zadané podmínce (nebo None, jestliže takový neexistuje).

elements vrátí seznam všech vyhovujících vnořených prvků.

element a elements používají stejnou syntaxi k zadání výběrové podmínky, která dává 3 možnosti, které lze používat a kombinovat: jQuery-like výrazy, přesná shoda hodnoty atributu, shoda pomocí regulárního výrazu.

Tady je jednoduchý příklad:

>>> a = DIV(DIV(DIV('a', _id='target',_class='abc')))
>>> d = a.elements('div#target')
>>> d[0][0] = 'změněný'
>>> print a
<div><div><div id="target" class="abc">změněný</div></div></div>

Nepojmenovaný argument elements metody je řetězec, který může obsahovat: jméno tagu; id tagu za nerovnítkem; jméno třídy za tečkou; explicitní hodnotu atributu v hranatých závorkách.

Tady jsou 4 možné způsoby, jak najdeme předchozí tag pomocí id:

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

Tady jsou 4 možné způsoby, jak najdeme předchozí tag pomocí jeho třídy:

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

Kterýkoli atribut (nejen id a class jako v příkladech) může být použit pro nalezení prvku, včetně více atributů ??/(the function element can take multiple named arguments), but only the first matching element will be returned./

Je možné zadat více kritérií, oddělených mezerou (musí být splněna všechna zadaná kritéria) nebo čárkou (stačí splnění některého z kritérií):

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

nebo se stejným významem

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

Jestliže hodnotu atributu specifikujete pojmenovaným argumentem, můžete použít buď řetězec nebo regulární výraz:

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

A speciální pojmenovaný argument DIVu a z něj zděděných helperů je find. Může být použit pro zadání hledané hodnoty nebo hledaného regulárního výrazu v textovém obsahu tagu. Například:

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

nebo

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

components

Tady je příklad, který vypisuje všechny prvky v html řetězci:

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

parent

parent vrací kontejner prvku:

>>> a = DIV(SPAN('a'),DIV('b'))
>>> d = a.element(find='a').parent
>>> d['_class']='abc'
>>> print a
<div class="abc"><span>a</span><div>b</div></div>

flatten

Metoda flatten rekurzivně serializuje obsah prvku na obyčejný text (bez tagů):

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

Metodě flatten Může být předán nepovinný argument, render, tedy funkce, která bude serializaci obsahu vykonávat odlišně. Tady je příklad, který serializuje některé tagy do Markmin wiki syntaxe:

>>> a = DIV(H1('titulek'), P('příklad ', A('odkazu', _href='#test')))
>>> from gluon.html import markmin_serializer
>>> print a.flatten(render=markmin_serializer)
## titulek

příklad [[odkazu #test]]

Jako součást Web2py je k dispozici markmin_serializer a markdown_serializer.

Parsování

TAG objekt může sloužit současně jako XML/HTML parser. Může číst text a konvertovat ho do stromové (tree) struktury helperů. To umožňuje manipulovat s obsahem, jak bylo popsáno výše:

>>> html = '<h1>Titulek</h1><p>tohle je <span>test</span></p>'
>>> parsed_html = TAG(html)
>>> parsed_html.element('span')[0]='TEST'
>>> print parsed_html
<h1>Title</h1><p>tohle je <span>TEST</span></p>

Vzhled stránky

page layout
layout.html
extent
include

Pohledy mohou doplňovat/rozšiřovat (extend) jiné pohledy (být vkládány do nich) a podobně mohou jiné pohledy obsahovat (include), celkově tedy může být vytvářena stromová hierarchie pohledů.

Například můžeme mít pohled "index.html", který rozšiřuje (vkládá se do) "layout.html" a sám obsahuje (vkládá) "body.html". Současně "layout.html" může vkládat "header.html" a "footer.html".

Kořenem stromu je to, co označujeme "layout view". Stejně jako všechny HTML šablony (pohledy) ho můžete (kromě jiných editorů) editovat ve webovém administračním rozhraní frameworku. Jméno souboru "layout.html" je jen konvence.

Tady je minimalistická stránka, která rozšiřuje "layout.html" view a sama vkládá view "page.html":

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

Soubor, na nějž odkazujeme pomocí extend, musí někde obsahovat {{include}} direktivu, například:

<html>
  <head>
    <title>Titulek stránky</title>
  </head>
  <body>
    {{include}}
  </body>
</html>

Po zavolání pohledu se načte extended (layout) view (rámcové view, do nějž má být první view vlloženo) a první view v něm nahradí {{include}} direktivu (rozvine se na jejím místě). Zpracování bude probíhat rekurzivně, dokud nebudou všechny extend a include direktivy nahrazeny. Poté bude výsledná šablona přeložena do Pythonu. Poznamenejme, že jestliže aplikaci bytecode kompilujete, je kompilován výsledný kód Pythonu, nikoli originální soubory pohledů. Proto bytecode kompilovanou verzí konkrétního view je jednoduchý .pyc soubor, který obsahuje Python kód nejen pro původní view, ale pro celou hierarchii (strom) odkazovaných extended a included pohledů.

extend, include, block a super jsou speciální direktivy šablon, nikoli příkazy Pythonu.

Veškerý obsah, který předchází {{extend ...}} direktivu, se vloží (a proto i vykoná) před vložením obsahu rozšiřujícího (extended) pohledu. Ačkoli toho typicky nevyužijete pro vkládání HTML obsahu, můžete to využít k nadefinování proměnných, které budou k dispozici a budou řídit rozšiřující šablonu. Například view "index.html" může vypadat takto:

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

a "layout.html":

{{if sidebar_enabled:}}
    <div id="sidebar">
        Sidebar Content
    </div>
{{pass}}

Protože přiřazení do sidebar_enabled proměnné v "index.html" předchází extend, vloží se tento řádek před začátek "layout.html" a sidebar_enabled proměnná bude dostupná všude v "layout.html" kódu (o něco sofistikovanější verze tohoto mechanismu je použita ve welcome aplikaci).

Poznamenejme také, že proměnné, které vrátila funkce kontroléru, jsou k dispozici nejen v pohledu, příslušném k oné akci, ale i ve všech pohlededch, které rozšiřuje nebo vkládá (extends/includes).

Parametrem extend a include (tj. jménem view, na které se odkazujeme)může být i proměnná (ne však výraz). Má to ale negativní důsledek. Je-li proměnná na místě odkazovaného pohledu použita, nemůže být takový pohled byte-kompilován. To proto, že, jak jsme již zmínili, byte-kompilované pohledy obsahují kompletní strom (hierarchii) odkazovaných view - a ta není známa, jestliže jméno view teprve předáme pomocí proměnné během runtime. Protože kompilované pohledy znamenají obvykle značný nárůst rychlosti odezvy, měli bychom si použití proměnných v extend a include příkazech spíše odpoustit.

V mnoha případech je alternativou k použití proměnné v příkazu include umístění obyčejného {{include ...}} do if...else bloku.

{{if some_condition:}}
{{include 'this_view.html'}}
{{else:}}
{{include 'that_view.html'}}
{{pass}}

Takový kód není pro bytecode kompilaci žádnou překážkou, protože proměnné v direktivách neobsahuje. Nicméně kompilace zahrne obě view, přestože při běhu programu bude použito jen jedno z nich - podle hodnotysome_condition.

Pamatujte, že toto pracuje právě jen pro include -- nemůžete umísťovat {{extend ...}} direktivy do if...else bloků.

response.menu
menu
response.meta
meta

Rámcové pohledy (layouts) se používají pro společnou funkcionalitu všech stránek webu (hlavičky, zápatí, menu) a proto jsou důležité - umožňují vaši aplikaci snáze napsat a udržovat. Konkrétně doporučujeme psát rámcové pohledy (layouts) pro následující proměnné, které můžete v kontroléru nastavit. Výběr právě této sady proměnných vám pomůže, aby více vámi připravených rámcových pohledů bylo vzájemně zaměnitelných:

response.title
response.subtitle
response.meta.author
response.meta.keywords
response.meta.description
response.flash
response.menu
response.files

S výjimkou menu a files jsou to všechno řetězce a jejich význam je jasný.

response.menu je seznam vektorů (tuples), a sice trojic nebo čtveřic. Jednotivé prvky vektoru jsou:

  1. Popis položky menu,
  2. True/False podle toho, zda odkaz je právě aktivní (zda je to aktuální odkaz),
  3. URL odkazované stránky.

Například:

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

Čtvrtý prvek vektoru je případné sub-menu (podřízené menu).

response.files je seznam CSS a JS souborů, které vaše stránka potřebuje.

Dále doporučujeme, abyste v HTML hlavičce použili:

{{include 'web2py_ajax.html'}}

protože tím připojíte jQuery knihovny a definujete některé javascriptové funkce pro speciální efekty a Ajax. "web2py_ajax.html" sestaví response.meta tagy hlavičky, připojí jQuery, kalendář pro výběr data, a všechny potřebné CSS a JS response.files soubory.

Defaultní rámcový vzhled (page layout)

superfish
ez.css

Tady je defaultní "views/layout.html", které dodáváme se vzorovou aplikací welcome (přičemž podle verze web2py se soubor může poněkud lišit). Každá nově založená aplikace bude mít takovýto rámcový vzhled (který se uplatní, pokud jej ponecháte a pokud vaše pohledy na něj budou odkazovat pomocí extend):

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>{{=response.title or request.application}}</title>

  <!-- http://dev.w3.org/html5/markup/meta.name.html -->
  <meta name="application-name" content="{{=request.application}}" />

  <script src="{{=URL('static','js/modernizr.custom.js')}}"></script>

  <!-- include stylesheets -->
  {{
  response.files.append(URL('static','css/skeleton.css'))
  response.files.append(URL('static','css/web2py.css'))
  response.files.append(URL('static','css/superfish.css'))
  response.files.append(URL('static','js/superfish.js'))
  }}

  {{include 'web2py_ajax.html'}}

  <script type="text/javascript">
    jQuery(function(){ jQuery('ul.sf-menu').supersubs({minWidth:12,maxWidth:30,extraWidth:3}).superfish(); });
  </script>

  {{
  # pro použití sidebarů ve vašem pohledu nejprve zadejte, který chcete použít
  left_sidebar_enabled = globals().get('left_sidebar_enabled',False)
  right_sidebar_enabled = globals().get('right_sidebar_enabled',False)
  middle_columns = {0:'sixteen',1:'twelve',2:'eight'}[
     (left_sidebar_enabled and 1 or 0)+(right_sidebar_enabled and 1 or 0)]
  }}

</head>
<body>
  <div class="wrapper"><!-- for sticky footer -->

    <div class="topbar">
      <div class="container">
        <div class="sixteen columns">
          <div id="navbar">
            {{='auth' in globals() and auth.navbar(separators=(' ',' | ',''))}}
          </div>
          <div id="menu">
            {{=MENU(response.menu, 
                    _class='mobile-menu' if is_mobile else 'sf-menu',
                    mobile=request.user_agent().is_mobile)}}
          </div>
        </div>
      </div>
    </div><!-- topbar -->

    <div class="flash">{{=response.flash or ''}}</div>

    <div class="header">
      <div class="container">
        <div class="sixteen columns">
          <h1 class="remove-bottom" style="margin-top: .5em;">
          {{=response.title or request.application}}
          </h1>
          <h5>{{=response.subtitle or ''}}</h5>
        </div>

        <div class="sixteen columns">
          <div class="statusbar">
            {{block statusbar}}
            <span class="breadcrumbs">{{=request.function}}</span>
            {{end}}
          </div>
        </div>
      </div>
    </div>

    <div class="main">
      <div class="container">
        {{if left_sidebar_enabled:}}
        <div class="four columns left-sidebar">
          {{block left_sidebar}}
          <h3>Left Sidebar</h3>
          <p></p>
          {{end}}
        </div>
        {{pass}}

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

        {{if right_sidebar_enabled:}}
        <div class="four columns">
          {{block right_sidebar}}
          <h3>Right Sidebar</h3>
          <p></p>
          {{end}}
        </div>
        {{pass}}

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

    <div class="push"></div>
  </div><!-- wrapper -->

  <div class="footer">
    <div class="container header">
      <div class="sixteen columns">
        {{block footer}} <!-- this is default footer -->
        <div class="footer-content" >
          {{=T('Copyright')}} &#169; 2011
          <div style="float: right;">
            <a href="http://www.web2py.com/">
            <img style="padding-bottom: 0;" 
                 src="{{=URL('static','images/poweredby.png')}}"/>
            </a>
          </div>
        </div>
        {{end}}
      </div>
    </div><!-- container -->
  </div><!-- footer -->

</body>
</html>

Tento Layout má některé vlastnosti, díky kterým je velmi snadno použitelný a přizpůsobitelný:

  • Je napsán v HTML5 a používá "modernizr" [modernizr] knihovnu pro zpětnou kompatibilitu. Obsahuje několik podmíněných příkazů (které jsme zde pro zjednodušení vynechali) pro přijatelnou činnost Microsoft IE.
  • Zobrazuje response.title a response.subtitle, které můžete nastavit v modelu (např. v menu.py). Není-li response.title nastaveno, zobrazuje jméno aplikace.
  • Vkládá web2py_ajax.html, které připojí všechny výše zmíněné odkazy a skripty.
  • Obsahuje upravenou verzi "skeleton" knihovny [skeleton] Je to knihovna pro flexibilní vzhled, která pracuje na mobilních zařízeních a aranžuje sloupce tak, aby byly dobře čitelné na malém displayi.
  • Používá "superfish.js" pro dynamická kaskádová menu. Explicitní skript pro aktivaci superfish kaskádových menu může být odstraněn, pokud není potřeba.
  • {{=auth.navbar(...)}} zobrazuje přivítání aktuálního uživatele a odkazy na funkce objektu auth, jako jsou login, logout, register, change password, apod., zobrazené odkazy se mění podle kontextu. Jedná se o helper a proto můžete s jeho prvky manipulovat stejně, jak bylo popsáno u helperů. Je umístěn do {{try:}}...{{except:pass}} struktury pro případ, že byste objekt auth nedefinovali.
  • {{=MENU(response.menu) zobrazuje strom menu pomocí <ul>...</ul>.
  • {{include}} se nahradí obsahem pohledu (view), které odpovídá právě prováděné akci a kontroléru (pokud obsahuje extend 'layout.html').
  • Připravuje 3-sloupcový design s tím, že zobrazení levého a pravého sidebaru můžete řídit nastavením odpovídajících proměnných ve vašem pohledu před zavoláním direktivy extend
  • Používá následující třídy: header, main, footer
  • Obsahuje následující bloky: statusbar, left_sidebar, center, right_sidebar, footer.

Pohled tedy může zapínat a plnit sidebary takto:

{{left_sidebar_enable=True}}
{{extend 'layout.html'}}

Tohle přijde doprostřed.

{{block left_sidebar}}
A tohle přijde do sidebaru.
{{end}}

Přizpůsobení (customizace) defaultního layoutu

CSS

Přizpůsobení defaultního layoutu bez editace je snadné, protože CSS soubory jsou dokumentované:

  • "bootstrap.min.css" obsahuje Twitter Boostrap CSS styl
    Bootstrap
  • "web2py.css" obsahuje styly specificky definované pro web2py

Např. pro změnu barev a obrázku pozadí zkuste vložit následující kód do layout.html hlavičky:

<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>

Samozřejmě můžete "layout.html" a "web2py.css" soubory úplně zaměnit jakýmikoli svými.

Vývoj pro mobilní zařízení

Defaultní layout.html je vytvořen, aby dobře spolupracoval s mobilními zařízeními. Ale to není dostatečné. Může být potřebné nebo vhodné použít jinou variantu pohledů, pokud je stránka navštívena z mobilního zařízení.

Pro usnadnění vývoje pro desktop a pro mobilní zařízení obsahuje web2py dekorátor @mobilize. Tento dekorátor slouží pro akce (funkce kontroléru), které mají mít normální a navíc mobilní pohled. Tady je příklad:

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

Všimněte si, že tento dekorátor je potřeba na začátku souboru kontroléru explicitně importovat. Je-li akce "index" zavolána z normálního prohlížeče (z desktop počítače), web2py bude renderovat výsledný slovník (dictionary) pomocí pohledu "[controller]/index.html". Ale pokud akci zavolá mobilní zařízení, slovník bude renderován pomocí "[controller]/index.mobile.html". Mobile pohledy tedy mají příponu "mobile.html".

Alternativně můžete pro všechny pohledy aktivovat jejich mobilní varianty (např. v modelu):

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

Jak vytvoříte "*.mobile.html" pohledy ponecháváme na vás jako vývojáři. Ale velmi doporučujeme použít "jQuery Mobile" plugin, který vám velmi pomůže.

Funkce v pohledech

Předpokládejme takovýto "layout.html":

<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{if 'mysidebar' in globals():}}{{mysidebar()}}{{else:}}
        můj defaultní sidebar
      {{pass}}
    </div>
  </body>
</html>

a tento pohled

{{def mysidebar():}}
můj nový sidebar !!!
{{return}}
{{extend 'layout.html'}}
Hello World!!!

Všimněte si, že funkce je definována před voláním {{extend...}} direktivy -- to způsobí, že funkce je vytvořena před vykonáním kódu z "layout.html", takže může kdekoli v něm být zavolána, a to i před {{include}}. Také si všimněte, že volání funkce je vloženo bez použití = prefixu.

Kód bude generovat následující výstup:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        můj nový sidebar !!!
    </div>
  </body>
</html>

Všimněte si, že funkce je definována pomocí HTML (ačkoli Python kód by mohla obsahovat), takže do výstupu se její obsah přepíše pomocí response.write (samotná funkce nevrací žádný obsah). Právě proto Layout volá tuto funkci pomocí {{mysidebar()}} místo pomocí {{=mysidebar()}}. Tímto způsobem definované funkce také mohou mít parametry.

Bloky v pohledech

block

Jiný způsob, jak udělat pohled více modulární, je použitím {{block...}} a tento mechanismus je jinou alternativou k dosud popsaným postupům.

Předpokládejme takovýto "layout.html":

<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{block mysidebar}}
        můj defaultní sidebar
      {{end}}
    </div>
  </body>
</html>

a pohled:

{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
  můj nový sidebar !!!
{{end}}

To bude generovat následující výstup:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        můj nový sidebar !!!
    </div>
  </body>
</html>

Počet bloků není omezen. Jestliže blok zapíšete jen do jednoho z pohledů, ale ve druhém stejnojmenný blok chybí, vypíše se i tak obsah bloku. Poznamenejme také, že na rozdíl od funkcí není nutné bloky definovat před {{extend ...}} -- bez ohledu na pořadí použití exclude/include a block direktiv mohou bloky provádět záměnu obsahu kdekoli.

super

Uvnitř bloku můžete ještě uvést {{super}} pro zdědění - zařazení obsahu obecné varianty bloku (rodiče). Například když pohled změníme takto:

{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
{{super}}
  můj nový sidebar !!!
{{end}}

tak dostaneme výstup:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        můj defaultní sidebar
        můj nový sidebar !!!
    </div>
  </body>
</html>
 top