Chapter 5: Widoki

Widoki

widoki
język szablonowania
HTML

W web2py do tworzenia modeli, kontrolerów i widoków wykorzystuje się Python, chociaż w widokach używa się nieco zmodyfikowaną składnię Pythona, aby umożliwić większą czytelność kodu bez nakładania jakichkolwiek ograniczeń na możliwości tego języka.

Przeznaczeniem widoku jest osadzenie kodu Pythona w dokumencie HTML. Zasadniczo sprawia to pewne problemy:

  • Jak opakowywać osadzany kod Pythona?
  • Czy dało by się uniknąć konieczności stosowania w kodzie wcięć, tak jak przewidują to zasady składniowe Pythona lub HTML?

W web2py używa się znacznik {{ ... }} do opakowanie kodu Pythona osadzanego w HTML. Zaletą używania nawiasów klamrowych zamiast nawiasów ostrych jest to, że są przejrzyste dla większości edytorów HTML. Umożliwia to wykorzystywanie edytorów HTML do tworzenia widoków web2py. Ograniczniki te można zmienić, na przykład tak

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

Jeśli ta linia zostanie umieszczona w modelu, to kod ten będzie dostępny wszędzie, jeśli w kontrolerze, to tylko w widokach dla akcji tego kontrolera, jeśli w akcji, to tylko w widoku dla tej akcji.

Ponieważ programista osadza kod Pythona w HTML, dokument powinien dostosowywać się do zasad HTML, a nie Pythona. Dlatego pozwalamy na niestosowanie wcięć wewnątrz znaczników {{ ... }}. Ponieważ Python zwykle używa wcięć do rozdzielenia bloków kodu, potrzebujemy na to inny sposób – to dlatego w szablonach web2py stosuje się słowo kluczowe Pythona pass.

Blok kodu rozpoczyna się od linii kończącej się znakiem dwukropka a kończy linią będącą słowem pass. Słowo kluczowe pass nie jest konieczne, gdy koniec bloku jest oczywisty (wynika z kontekstu).

Oto przykład:

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

Trzeba mieć na uwadze, że słowo pass jest słowem kluczowym Pythona, a nie słowem kluczowym web2py. Niektóre edytory Pythona, takie jak Emacs, używają słowa kluczowego pass do oznaczania podziału bloków i wykorzystują go do automatycznego odtworzenia wcięć bloków.

Język szablonowania web2py robi dokładnie to samo. Gdy znajdzie coś takiego:

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

przekształca to do programu:

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)

Metoda response.write dokonuje zapisu do response.body.

Gdy w widoku web2py pojawi się błąd, widok generuje raport błędu, a nie rzeczywisty widok zakodowany przez programistę. Pomaga to programiście debugować kod przez podświetlanie rzeczywistego kodu, który jest wykonywany (kodu, który może być debugowany w edytorze HTML lub inspektorze DOM przeglądarki).

Należy również pamiętać, że:

{{=x}}

generuje

response.write
zabezpieczanie zmiennych

response.write(x)

Zmienne wstrzykiwane do HTML w ten sposób są domyślnie zabezpieczane znakami ucieczki. Zabezpieczanie jest ignorowane, jeśli x jest obiektem XML, nawet jeśli parametr escape jest ustawiony na True.

Oto przykład wprowadzający helper H1:

{{=H1(i)}}

który jest łumaczony na:

response.write(H1(i))

Po sprawdzeniu, obiekt H1 i jego elementy zostają rekursywnie serializowane, zabezpieczane znakami ucieczki i zapisywane do ciała odpowiedzi. Znaczniki generowane przezH1 i wewnętrzny kod HTML nie są zabezpieczane znakami ucieczki. Ten mechanizm gwarantuje, że cały tekst (i tylko tekst) wyświetlany na stronie jest zawsze zabezpieczany znakami ucieczki, chroniąc w ten sposób przed atakami XSS. Jednocześnie kod jest prosty i łatwy do debugowania.

Metoda response.write(obj, escape=True) pobiera dwa argumenty, obiekt do zapisu i argument wskazujący, czy tekst w obiekcie ma być zabezpieczony znakami ucieczki (domyślnie ustawiony na True). Jeśli obj ma metodę .xml(), to jest ona wywoływana a wynik zapisywany do ciała odpowiedzi (argument escape jest ignorowany). W przeciwnym razie wykorzystywana jest metoda __str__ tego obiektu w celu jego serializacji i jeśli argument escape jest ustawiony na True, następuje zabezpieczenie tekstu znakami ucieczki. Wszystkie wbudowane helpery HTML (H1 w tym przykładzie) są obiektami, które wiedzą jak serializować same siebie poprzez metodę .xml().

Wszystko to odbywa się w sposób transparenty. Nigdy nie trzeba (i nie powinno się) wywoływać jawnie metody response.write.

Podstawy składni

Język szablonowania web2py obsługuje wszystkie instrukcje sterujące języka Python. Podano tutaj kilka przykładów każdej z nich. Mogą być one zagnieżdżane według zwykłej praktyki programowania.

Instrukcja for...in

for

W szablonie można zapętlić każdy iterowalny obiekt:

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

co wytworzy:

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

Tutaj item jest dowolnym iterowalnym obiektem, takim jak lista Pythona, krotka Pythona lub obiekt Rows albo każdy inny obiekt, który jest implementowany jako iterator. Wyświetlane elementy są najpierw serializowane a następnie zabezpieczane znakami ucieczki.

Instrukcja while

while

Można utworzyć pętlę wykorzystując słowo kluczowe while:

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

co wyprodukuje:

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

Instrukcja if...elif...else

if
elif
else

Można użyć klauzul warunkowych:

{{
import random
k = random.randint(0, 100)
}}
<h2>
{{=k}}
{{if k % 2:}}is odd{{else:}}is even{{pass}}
</h2>

co wyprodukuje:

<h2>
45 is odd
</h2>

Ponieważ oczywistym jest, że else zamyka blok if, to nie ma potrzeby stosować wyrażenie pass i użycie tego byłoby nieprawidłowe. Jednakże trzeba jawnie zamknąć blok else słowem pass.

Przypominamy, że w Pythonie wyrażenie "else if" jest pisane jako elif, tak jak w następującym przykładzie:

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

Wyprodukuje to:

<h2>
64 is divisible by 4
</h2>

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

try
except
else
finally

W widoku można również użyć wyrażenia try...except z jednym zastrzeżeniem. Rozważmy następujący przykład:

{{try:}}
Hello {{= 1 / 0}}
{{except:}}
division by zero
{{else:}}
no division by zero
{{finally}}
<br />
{{pass}}

Wyprodukuje do następujące wyjście:

Hello
division by zero
<br />

Przykład ten ilustruje, że dane wyjściowe generowane przed zgłoszeniem wyjątku są renderowane (łącznie z danymi przetworzonymi przed zgłoszeniem wyjątku) wewnątrz bloku try. "Hello" zostaje napisane ponieważ poprzedza wyjątek.

Instrukcja def...return

def
return

Język szablonowania web2py umożliwia programistom definiowanie i implementowanie funkcji, które mogą zwracać jakikolwiek obiekt Pythona lub łańcuch tekstowy (w tym kod HTML). Przyjrzyjmy się dwóm przykładom:

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

wytworzy następujące wyjście:

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

Funkcja itemize1 zwraca obiekt helpera, który jest wkładany w kod w miejscu gdzie funkcja została wywołana.

Rozwazmy poniższy kod:

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

Wytworzy to dokładnie takie samo wyjście jak w powyższym przykładzie. W tym przypadku, funkcja itemize2 reprezentuje porcję kodu HTML, który ma zastąpić znacznik web2py w którym funkcja została wywołana. Proszę zwrócić uwagę, że nie ma znaku '=' przed wywołaniem itemize2, ponieważ funkcja ta nie zwraca tekstu, ale zapisuje dane wyjściowe bezpośrednio do odpowiedzi.

Jest jedno zastrzeżenie: funkcje definiowane wewnątrz widoku muszą kończyć się wyrażeniem return albo automatyczne wcięcie zawiedzie.

Helpery HTML

helpery html

Rozważmy następujący kod widoku:

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

jest on zwracany jako:

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

DIV jest klasą helpera HTML,czyli czymś co może zostać zastosowane do programowego zbudowania kodu HTML. Odpowiada on znacznikowi <div> HTML.

Argumenty pozycyjne są interpretowane jak obiekty zawarte pomiędzy znacznikiem otwierającym a zamykającym HTML. Argumenty nazwane rozpoczynające się znakiem podkreślenia są interpretowane jako atrybuty znacznika HTML (bez znaków podkreślenia). Niektóre helpery maja również nazwane argumenty nie rozpoczynające się znakiem podkreślenia – argumenty te są specyficzne dla znacznika.

Zamiast ustawienia nienazwanych argumentów, można zrobić tak, że helper pobierze pojedynczą listę lub krotkę jako swój zestaw komponentów, gdy użyje się notacji ze znakiem gwiazdki * oraz tak, że pobierze pojedynczy słownik jako zbiór atrybutów, gdy użyje się dwóch gwiazdek **, na przykład:

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

produkuje to samo, co poprzedni kod.

Helpery:

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

mogą być używane do budowy złożonych wyrażeń, które mogą być serializowane do XML[xml-w] [xml-o]. Na przykład:

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

zostanie renderowane tak:

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

Helpery można również serialiować do łańcuchów tekstowych, równoważnie z metodami __str__ i xml:

>>> print str(DIV("hello world"))
<div>hello world</div>
>>> print DIV("hello world").xml()
<div>hello world</div>

obiektowy model dokumentu
Document Object Model (DOM)
Mechanizm helperów w web2py jest czymś więcej niż tylko systemem do generowania kodu HTML bez łączenia ciągów znakowych. Zapewnia reprezentację obiektowego modelu dokumentu (Document Object Model - DOM) po stronie serwera.

Komponenty helperów mogą być odnoszone poprzez ich pozycję a helpery działają jak listy w stosunku do ich komponentów:

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

Atrybuty helperów mogą być odnoszone przez nazwę a helpery działają jak słownik w stosunku do ich atrybutów:

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

Uwaga, kompletny zestaw komponentów może być dostępny poprzez listę o nazwie a.components a kompletny zestaw atrybutów poprzez słownik o nazwie a.attributes. Tak więc a[i] jest równoważnikiem a.components[i] gdzie i to liczba całkowita, natomiast a[s] jest równoważnikiem a.attributes[s] gdzie s jest ciągiem znakowym.

Atrybuty helpera są przekazywane do helpera jako argumenty kluczowe. Jednak w niektórych przypadkach nazwy atrybutów zawierają specjalne znaki, które nie są dozwolone w identyfikatorach Pythona (np. myślniki) i dlatego nie mogą być użyte jako nazwy argumentów kluczowych. Na przykład:

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

nie będzie działać, ponieważ "_data-role" zawiera myślnik, który będzie powodował błąd składni Pythona.

W takich przypadkach ma się kilka możliwości. Można zastosować argument data (tym razem bez wiodącego znaku podkreślenia) do przekazania słownika związanych atrybutów bez ich wiodącego myślnika a wyjście będzie miało pożądaną kombinacje danych np.

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

lub można zamiast tego przekazać atrybuty jako słownik i wykorzystać notację dwóch gwiazdek (**) argumentów funkcji Pythona, co odwzoruje słownik par (klucz:wartość) na zestaw argumentów kluczowych:

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

Bardziej skomplikowane zapisy mogą zawierać znaki, które powinny być na wyjściu zamienione na znaki encji HTML, np.

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

Można również dynamicznie utworzyć specjalny TAG:

>>> 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 to obiekt stosowany do hermetyzowania tekstu, który nie powinien być zabezpieczany sekwencjami ucieczki. Tekst ten może, ale nie musi, zawierać prawidłowy kod XML - na przykład, może zawierać kod JavaScript.

Tekst w tym przykładzie jest zabezpieczany encjami znakowymi XML:

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

stosując XML można się zabezpieczyć przed wstawianiem w tekstu sekwencji ucieczki:

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

Czasem zachodzi potrzeba renderowania kodu HTML zapisanego w zmiennej, ale kod taki może zawierać niebezpieczne znaczniki, takie jak script:

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

Nie zabezpieczenie wykonywalnego kodu, takiego jak ten (na przykład, wprowadzonego w treści komentarza na blogu) jest niebezpieczne, ponieważ może zostać użyty do wygenerowania ataku Cross Site Scripting (XSS) na strony innych odwiedzających.

sanitize

Helper XML web2py może zabezpieczyć tekst przed iniekcją poprzez wstawienie encji znakowych przy wszystkich znacznikach, z wyjątkiem tych, które wskazało się jawnie. Oto przykład:

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

Konstruktory XML traktują domyślnie zawartość niektórych znaczników i niektóre z ich atrybutów za bezpieczne. Można zastąpić domyślne ustawienie opcjonalnych argumentów permitted_tags i allowed_attributes. Oto domyślne wartości opcjonalnych argumentów helpera XML.

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

Helpery wbudowane

A

Helper ten jest używany do tworzenia odnośników.

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>

Zamiast _href można przekazać adres URL wykorzystując argument callback. Oto przykład fragmentu widoku:

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

skutkujący tym, że po kliknięciu odnośnika wywołany zostanie kod ajax "myaction" zamiast kod przekierowania. W tym przypadku, opcjonalnie można podać dwa argumenty: target i delete:

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

Odpowiedź wywołania zwrotnego ajax zostanie zapisana w obiekcie DIV z atrybutem id równym "t".

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

W odpowiedzi, najbliższy znacznik dopasowany do "div#b" zostanie usunięty. W tym przypadku usunięty zostanie przycisk. Typowym zastosowanie to użycie w tablicy:

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

Naciśnięcie przycisku wykona wywołanie zwrotne i usunie wiersz tablicy.

callback i delete można łączyć.

Helper A pobiera specjalny argument o nazwie cid. Działa on następująco:

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

Kliknięcie na na link spowoduje załadowanie treści w znaczniku div. Jest to podobne, ale silniejsze niż powyższa składnia, ponieważ jest zaprojektowane do odświeżania treści strony. Omówimy zastosowanie cid bardziej szczegółowo w rozdziale 12, w kontekście komponentów.

Te funkcjonalności ajax wymagają dostępu do jQuery i skryptu "static/js/web2py_ajax.js", które są automatycznie dołączane poprzez umiejscowienie w układzie head wyrażenia {{include 'web2py_ajax.html'}}. Skrypt "views/web2py_ajax.html" definuje kilka zmiennych opartych na request i zawiera wszystkie niezbędne pliki js i css.

B

B

Helper ten dokonuje pogrubienia tekstu.

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

Helper ten umieszcza ciało strony.

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

BR

BR

Helper ten tworzy podział wiersza.

>>> print BR()
<br />

Warto pamiętać, że w helperach istnieje możliwość powtarzania wyjścia przy wykorzystaniu operatora mnożenie:

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

CAT

CAT

Helper ten łączy inne helpery, takie jak TAG[''].

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

Helper ten powoduje wyśrodkowanie tekstu.

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

Powoduje podświetlanie składni kodu Pythona, C, C++, HTML i web2py, co ulepsza kod wykazywany w PRE. Helper CODE ma również zdolność tworzenia odnośników do dokumentacji API web2py.

Oto przykład z podświetlaniem kodu Pythona:

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

Oto podobny przykład z 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>

Helper CODE ma domyślne argumenty:

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

Obsługiwanymi wartościami dla argumentu language są "python", "html_plain", "c", "cpp", "web2py" i "html". Język "html" interpretuje znaczniki {{ i }} jako kod "web2py", natomiast "html_plain" nie.

Jeśli określona jest wartość link, na przykład "/examples/global/vars/", API web2py dokona odniesienia w kodzie umieszczając odnośnik URL do dokumentacji. Na przykład "request" zostanie zlinkowany z "/examples/global/vars/request". W powyższym przykładzie, odnośnik URL jest obsługiwany przez przez akcję "vars" w kontrolerze "global.py", który jest rozpowszechniany jako część aplikacji "examples" web2py.

Argument counter jest używany dla numerowania linii kodu. Może zostać ustawiony na jedną z trzech możliwych wartości. Może to być None wskazująca na brak numeracji, wartość numeryczna wskazująca na numer początkowy oraz ciąg znakowy. Jeśli argument ten jest ustawiony na jakiś ciąg znakowy, to jest on traktowany jako znak zachęty w kodzie i linie kodu nie są numerowane.

Argument styles jest nieco skomplikowany. Jeśli spojrzy się na powyżej wygenerowany kod HTML, to widać, żezawiera on tabelę z dwoma kolumnami, przy czym każda kolumna ma określony wierszowo swój własny styl CSS. Atrybuty styles umożliwiają nadpisać te dwa domyślne style CSS. Na przykład:

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

Atrybut styles musi być słownikiem umożliwiającym określenie dwóch kluczy: CODE dla stylu rzeczywistego kodu i LINENUMBERS dla stylu kolumny z numeracją wierszy. Warto pamiętać, że style te całkowicie wymieniają domyślne style a nie są dodawane do stylów domyślnych.

COL

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

COLGROUP

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

DIV

Wszystkie helpery, z wyjątkiem XML, pochodzą od DIV i dziedziczą jego podstawowe 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

Uwydatnia treść, zmieniając czcionkę najczęściej na italik (interpretacja znacznika em zależy od przeglądarki).

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

Jest używany do tworzenia pola wejściowego wraz z etykietą.

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

FORM

FORM

Jest to jeden z najważniejszych helperów. W bardzo prosty sposób tworzy znacznik <form>...</form>, ale ponieważ helper ten jest obiektami i posiada wiedzę o tym co zawiera, to może przetwarzać zgłoszone formularze (na przykład, wykonywać walidację pól). Jest to szczegółowo omówione w rozdziale 7.

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

Domyślnie "enctype" to "multipart/form-data".

hidden

Konstruktor FORM i SQLFORM mogą pobierać argument o nazwie hidden. Gdy słownik jest przekazywany jako hidden, to jego elementy są przekształcane do pola INPUT "hidden". Na przykład:

>>> 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 te są nagłówkami paragrafów i podparagrafów:

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

HEAD

Do wygenerowania znaczników sekcji HEAD strony HTML.

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

HTML

HTML
XHTML

Helper ten jest trochę inny. Oprócz generowania znaczników <html>, poprzedza znacznik ciągiem doctype[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>

Helper HTML pobiera kilka opcjonalnych argumentów, która mają następujące wartości domyślne:

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

gdzie argument doctype może mieć wartość 'strict', 'transitional', 'frameset', 'html5' lub jakiś pełny ciąg doctype.

XHTML

XHTML

XHTML jest podobny do HTML ale tworzy tworzy doctype XHTML a nie HTML.

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

gdzie argument doctype może mieć wartość 'strict', 'transitional', 'frameset' lub jakiegoś pełnego ciągu doctype.

HR

HR

Helper ten tworzy na stronie HTML poziomą linię

>>> print HR()
<hr />

I

I

Tworzy treść pisaną italikiem.

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

IFRAME

Helper ten zawiera inną stronę internetową w bieżącej stronie. Adres URL innej strony jest określany poprzez atrybut "_src".

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

IMG

IMG

Używany jest do osadzania obrazów w HTML:

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

Oto połączenie helperów A, IMG i URL użyte w celu zawarcia statycznego obrazu z linkiem:

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

Tworzy znacznik <input.../>. Znacznik input może nie zawierać innych znaczników i jest zamykany znakami /> zamiast >. Znacznik input ma opcjonalny atrybut _type, który można ustawić na "text" (domyślnie), "submit", "checkbox" lub "radio".

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

Helper ten pobiera opcjonalny specjalny argument o nazwie "value", odrębny od argumentu "_value". Ten ostatni ustawia domyślną wartość dla pola input. Pierwszy ustawia bieżącą wartość. Dla pola input typ "text", pierwszy argument zastępuje drugi:

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

Dla przycisków radio INPUT selektywnie ustawia atrybut "checked":

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

i podobnie dla pól wyboru:

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

LABEL

Jest używany do tworzenia znacznika <label> dla pól formularza.

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

Jest używany do tworzenia znacznika <legend> dla pól formularza.

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

LI

Tworzy element listy (znacznik <li>), który powinien być zawarty w znacznikach <ul> lub <ol>.

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

Używany do budowania znaczników <meta> w sekcji HEAD. Na przykład:

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

MARKMIN

Implementuje składnię wiki markmin. Konwertuje tekst wejściowy do wyjścia HTML zgodnie z poniższym przykładem:

MARKMIN
>>> print MARKMIN("this is **bold** or ''italic'' and this [[a link http://web2py.com]]")
<p>this is <b>bold</b> or <i>italic</i> and
this <a href="http://web2py.com">a link</a></p>

Składnia markmin jest opisana w pliku:

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

dostarczanym wraz z web2py.

Markmin można używać do generowania dokumentów HTML, LaTeX i 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

(helper MARKMIN jest skrótem dla markmin2html)

Oto podstawowa składnia:

ŹRÓDŁOWYJŚCIE
# titletytuł
## sectionrozdział
### subsectionpodrozdział
**bold**gruby
''italic''italik
``verbatim``dosłowny
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>
[[click me #myanchor]]kliknij mnie
[[myanchor]]Utworzenie kotwicy dla odnośnika
$$\int_a^b sin(x)dx$$
Odnośniki MARKMIN

Odnośniki mają postać: [[wyświetlany tekst odnośnika <link>]]. <link> może być kotwicą np. #myanchor lub adresem URI np. http://www.web2py.com lub względną referencją np. [[patrz rodział 8 ../08]] lub [[patrz rozdział 8 ../08#myanchor]]

Wystarczy dołączyć odnośnik do pliku obrazu, wideo lub audio bez wynikowego znacznika a zostanie automatycznie dołączony odpowiedni plik obrazu, wideo lub audio (dla pliku audio i video będzie użyty odpowiednio znacznik <audio> i <video>).

Dodanie odnośnika z przedrostkiem qr:, tak jak tu

qr:http://web2py.com

spowoduje, że osadzony zostanie odpowiedni kod QR i zlinkowany z określonym adresem URL.

Dodanie odnośnika z przedrostkiem embed:, tak jak tu

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

spowoduje, osadzenie określonej strony, w tym przypadku wideo youtube.

Obrazy mogą być również osadzane na następującą składnią:

[[image-description http://.../image.png right 200px]]
Listy i tabele MARKMIN

Listy nieuporządkowane tworzy się używajac znak myślnika przed każdym elementem listy:

- one
- two
- three

Listy uporządkowane tworzy się stosując znak plus przed każdym elementem listy:

+ one
+ two
+ three

Natomiast tablice w ten sposób:

----------
 X | 0 | 0
 0 | X | 0
 0 | 0 | 1
----------
Rozszerzanie MARKMIN

Składnia MARKMIN obsługuje również znaczniki bloków cytatów (ang. blockquotes), znaczniki audio i wideo HTML, wyrównywanie obrazów, własne style CSS i to wszystko można rozszerzać:

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

generuje

'cbcb'

Niestandardowe bloki są ograniczane przez ``...``:<key> i są renderowane przez funkcję przekazywaną jako odpowiedni klucz w słowniku argumentu extra MARKMIN. Trzeba pamiętać, aby zabezpieczyć funkcję znakami ucieczki w celu zapobieżenia atakom XSS.

OBJECT

Używany do osadzania w HTML obiektów (na przykład flash player).

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

Wskazuje na listę uporządkowaną. Lista powinna zawierać znaczniki LI. Argumenty OL, które nie są obiektami LI są automatycznie otaczane znacznikami <li>...</li>.

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

Znajduje się tutaj dla zachowania wstecznej kompatybilności i jest po prostu aliasem True. Jest stosowany wyłącznie dla pól wyboru i jest przestarzały, ponieważ True jest bardziej poprawne dla języka Python.

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

OPTGROUP

Pozwala na grupowanie wielu opcji w bloku SELECT i jest użyteczne dla dostosowywania pól z użyciem 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

Używać się to powinno tylko jako część kombinacji SELECT/OPTION.

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

Tak jak w przypadku INPUT, web2py rozróżnia między atrybutami "_value" (wartość OPTION) i "value" (aktualna wartość "select"). Jeśli są równe, opcja ma wartość "selected".

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

P

P

Helper ten generuje znacznik początkowy i końcowy akapitu, <p>...</p>.

>>> 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 znacznik <pre>...</pre> dla wyświetlania tekstu preformatowanego. Helper CODE jest ogólnie korzystniejszy dla wyświetlania kodu.

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

Dołącza albo linkuje skrypt, taki jak JavaScript. Dla bardzo starych przeglądarek zawartość pomiędzy znacznikami jest renderowana jako komentarz HTML.

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

SELECT

SELECT

Generuje znacznik <select>...</select>. Jest używany razem z helperemOPTION. Argumenty SELECT, które nie są obiektami OPTION są automatycznie konwertowane do opcji.

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

Podobny do DIV, ale używany do wygenerowania wierszowego znacznika treści <span> ...</span>.

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

Podobny do SCRIPT, ale używany do dołączania albo linkowania kodu CSS. Oto jak dołącza się kod CSS:

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

a oto jak jest linkowany plik CSS:

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

TABLE, TR, TD

TABLE
TR
TD

Helpery te (wraz z opcjonalnymi THEAD, TBODY i TFOOTER) są używane do tworzenia tabel HTML.

>>> 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 oczekuje zawartości TD. Argumenty, które nie są obiektami TD są konwertowane automatycznie.

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

Konwersję tablicy Pythona do tabeli HTML można dokonać łatwo przy wykorzystaniu notacji z gwiazdka (*) argumentów funkcji, która odwzorowuje elementy list na argumenty pozycyjne funkcji.

Tutaj zrobimy to linia za linią:

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

Tutaj zrobimy wszystkie linie naraz:

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

Jest wykorzystywany do wygenerowania znacznika określającego obszar wierszy zawartych w ciele tabeli, w odróżnieniu do wierszy nagłówka i stopki. Nie jest to element obowiązkowy tabel.

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

TEXTAREA

TEXTAREA

Helper ten generuje znacznik <textarea>...</textarea>.

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

Jedynym ograniczeniem jest to, że jego opcjonalny argument "value" zastępuje treść znacznika <textarea> ... </textarea> (wewnątrz HTML)

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

TFOOT

TFOOT

Jest stosowany do zaznaczenia obszaru wierszy stopki tabeli.

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

TH

TH

Jest używany zamiast TD w nagłówku tabeli.

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

Jest używany do określenia obszaru wierszy nagłówka tabeli.

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

TITLE

TITLE

Jest używany do wygenerowania znacznika <title> w sekcji header strony HTML.

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

TR

TR

Generuje znaczniki wierszy tabeli. Należy umieszczać wewnątrz tabeli. Powinny zawierać znaczniki <td>...</td>. Argumenty TR, które nie są obiektami TD będą automatycznie przekonwertowane.

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

Generuje znaczniki do wyświetlania tekstu stałej szerokości.

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

UL

Generuje znacznik nieuporządkowanej listy i wymaga umieszczenie wewnątrz elementów LI. Jeśli jego zawartość nie posiada obiektów LI, UL doda je automatycznie.

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>

URL

Helper URL jest udokumentowany w rozdziale 4

embed64

embed64(filename=None, file=None, data=None, extension='image/gif') szyfruje dostarczone dane (binarne) do base64.

  • filename: otwiera i odczytuje plik o podanej nazwie w trybie 'rb', jeśli podano;
  • file: odczytuje wskazany plik, jeśli podano;
  • data: wykorzystuje podane dane do zaszyfrowania, jeśli podano.
embed64

xmlescape

xmlescape(data, quote=True) zwraca ciąg znakowy dostarczonych danych, zabezpieczony znakami ucieczki.

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

Helpery indywidualne

TAG

TAG

Czasem zachodzi potrzeba wygenerowania własnych znaczników XML. W web2py dostarczany jest helper TAG - uniwersalny generator znaczników.

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

generuje następujący kod XML

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

Argumenty "a", "b" i "d" są automatycznie zabezpieczane znakami ucieczki. Do stłumienia tego zachowania użyty zostaje helper XML. Stosując TAG można wygenerować znaczniki HTML/XML nie dostarczane przez API. Helpery TAG być mogą być zagnieżdżane i serializowane poprzez str(). Równoważna składnia to:

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

Jeśli obiekt TAG zostanie utworzony z pustą nazwą, to może być wykorzystany do łączenia wielu ciągów znakowych i helperów HTML, razem z zawarciem ich otaczającym je znaczniku, lecz używanie tego jest zdeprecjonowane na rzecz helpera CAT.

Za pomocą helpera TAG można generować znaczniki samozamykające. Ich nazwa musi się kończyć znakiem ukośnika ("/").

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

generuje następujący kod XML:

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

Proszę zwrócić uwagę, że TAG jest obiektem a TAG.name lub TAG['name'] jest funkcja zwracająca tymczasową klasę helpera.

MENU

MENU

Helper MENU pobiera listę list lub krotkę formularzy response.menu (tak jak opisano to w rozdziale 4) i generuje drzewiastą strukturę używającą nieuporządkowanych list reprezentujących menu. Na przykład:

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

Trzeci element w każdej liście (krotce) może być helperem HTML (który może zawierać zagnieżdżone helpery) a wtedy renderowany będzie helper MENU, zamiast tworzenia własnego znacznika <a>.

Każdy element menu może mieć czwarty argument, który jest zagnieżdżonym podmenu (i rekursywnie tak dalej):

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

Element menu może mieć również opcjonalny piąty element, który ma wartość logiczną. Gdy ta wartość wynosi false, to ten element menu jest ignorowany przez helper MENU.

Helper MENU pobiera następujące opcjonalne argumenty:

  • _class: ustawia klasę zewnętrznych elementów UL, domyślnie "web2py-menu web2py-menu-vertical";
  • ul_class: ustawia klasę wewnętrznych elementów UL, domyślnie "web2py-menu-vertical";
  • li_class: ustawia klasę wewnętrznych elementów LI, domyślnie "web2py-menu-expand";
  • li_first: pozwala dodać klasę do pierwszego elementu listy;
  • li_last: pozwala dodać klasę do ostatniego elementu listy.
urządzenia mobilne

MENU pobiera opcjonalny argument mobile. Gdy zostanie ustawiony na True, to zamiast budowania menu o rekursywnej strukturze UL, zwracane jest rozwijane menu SELECT ze wszystkimi opcjami menu i atrybutem onchange, który przekierowuje do strony odpowiadającej wybranej opcji. Jest to zaprojektowane jako alternatywne reprezentacja menu, która zwiększa użyteczność dla małych urządzeń mobilnych, takich jak telefony.

Zwykle menu jest używane w układzie z następującą składnią:

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

W ten sposób urządzenie mobilne jest automatycznie wykrywane i odpowiednio renderowane jest menu.

BEAUTIFY

Helper BEAUTIFY wykorzystywany jest do budowania reprezentacji HTML złożonych obiektów, w tym list, krotek i słowników:

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

BEAUTIFY zwraca obiekt podobny do XML serializowalny do XML, z ładnie wyglądającą reprezentacją argumentu swojego konstruktora. W tym przypadku reprezentacja XML:

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

zostanie zrenderowana tak:

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

Model DOM po stronie serwera a parsowanie

element
elements

elements

Helper DIV oraz wszystkie helpery pochodne udostępniają metody wyszukujące element i elements.

Metoda element zwraca pierwszy element podrzędny dopasowany do określonego warunku (lub None jeśli nic nie zostanie dopasowane).

Metoda elements zwraca wszystkie dopasowane elementy podrzędne.

Metody element i elements używają tej samej składni do określenia warunków dopasowania, dopuszczającej trzy możliwości, które mogą być mieszane i dopasowywane: wyrażenia takie jak jQuery, dopasowanie przez dokładną wartość atrybutu i dopasowanie przy użyciu wyrażeń regularnych.

oto prosty przykład:

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

Nienazwany argument metody elements jest ciągiem znakowym, który może zawierać: nazwę znacznika, id znacznika poprzedzonego znakiem funta, nazwą klasy poprzedzoną kropka, jawną wartością atrybutu w nawiasach kwadratowych.

Oto 4 równoważne sposoby wyszukiwania poprzedniego znacznika poprzez id:

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

Oto 4 równoważne sposoby wyszukiwania poprzedniego znacznika poprzez klasę:

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

Do lokalizacji elementu może zostać użyty każdy atrybut (nie tylko id czy class), włączając w to wszystkie nazwane atrybuty (element funkcji może pobierać wiele argumentów nazwanych), ale tylko pierwszy element zostanie zwrócony.

Korzystając ze składni jQuery "div#target" możliwe jest określenie wielu kryteriów wyszukiwania rozdzielanych przecinkiem:

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

lub równoważnie:

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

Jeśli wartość atrybutu została określona przy użyciu nazwanego argumentu, to może być ciągiem znakowym lub wyrażeniem regularnym:

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

Specjalnym nazwanym argumentem helpera DIV (i helperów pochodnych) jest find. Można go stosować do określenia wyszukiwanej wartości lub wyrażenia regularnego w treści tekstu znacznika. Na przykład:

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

lub

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

components

Oto przykład uzyskania wykazu wszystkich elementów w łańcuchu html:

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

parent
sibling

parent i siblings

parent zwraca element nadrzędny bieżącego elementu.

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

Zastępowanie elementów

Dopasowane elementy można również wymieniać lub usuwać podając argument replace. Lista oryginalnie dopasowanych elementów jest zwracana nadal w zwykły sposób.

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

Argument replace może być wywoływalny. W takim przypadku przekazuje on oryginalny element i oczekiwany jest zwrot zamienionego elementu:

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

Jeśli replace=None, to dopasowane elementy zostaną całkowicie usunięte.

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

flatten

Metoda flatten rekursywnie serializuje zawartość elementów podrzędnych danego elementu do zwykłego tekstu (bez znaczników):

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

Metoda flatten może przekazywać opcjonalny argument render, czyli funkcję, która renderuje (spłaszcza) zawartość przy użyciu innego protokołu. Oto przykład serializacji kilku znaczników do składni wiki Markmin:

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

W chwili pisania tego rozdziału udostępniamy markmin_serializer i markdown_serializer.

Parsowanie

Obiekt TAG jest również parserem XML/HTML. Może odczytywać tekst i konwertować do struktury drzewiastej helperów. Pozwala to na manipulowanie przy wykorzystaniu powyższego API:

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

układ strony
layout.html
extent
include

Układ strony

Widoki można rozszerzać i wstawiać jeden widok w drugi tworząc drzewiastą strukturę.

Na przykład widok "index.html" rozszerza "layout.html" i wstawiany jest w nim "body.html". Równocześnie w "layout.html" wstawiany jest "header.html" i "footer.html".

Korzeniem tego drzewa jest to co nazywamy układem strony (ang. page layout). Można go edytować, jak każdy inny plik szablonowy HTML, wykorzystując interfejs administracyjny web2py. Nazwa pliku "layout.html", to tylko konwencja.

Oto minimalna strona rozszerzająca widok "layout.html" i zawierająca widok "page.html":

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

Plik układu możliwego do rozszerzania musi zawierać dyrektywę {{include}}, podobnie jak tu:

<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    {{include}}
  </body>
</html>

Podczas wywoływania widoku ładowany jest widok (układ) będący bazą rozszerzenia oraz wywoływany jest wewnątrz tego układu widok zamieniający dyrektywę {{include}}. Przetwarzanie jest kontynuowane dopóki wszystkie dyrektywy extend i include nie zostaną przetworzone. Wynikowy szablon jest następnie tłumaczony na kod Pythona. Proszę mieć na uwadze, że gdy aplikacja jest skompilowana do kodu bajtowego, jest to kod Pythona, który został skompilowany, a nie oryginalne pliki widoku. Tak więc skompilowana do kodu bajtowego wersja określonego widoku jest pojedynczym plikiem .pyc, obejmującym całe drzewo rozszerzonych i wstawianych widoków.

Dyrektywy extend, include, block i super są specjalnymi dyrektywami szablonowymi a nie poleceniami Pythona.

Każda treść poprzedzający dyrektywę {{extend ...}} zostanie wstawiona (i przetworzona) przed rozpoczęciem przetwarzania rozszerzonej (bazowej) treści widoku. Chociaż nie jest to zwykle używane do wstawiania rzeczywistej zawartości HTML, może być użyteczne do określenia zmiennych lub funkcji, które chce się uczynić dostępnymi dla rozszerzanego widoku. Na przykład, rozważmy widok "index.html":

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

i fragment z "layout.html":

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

Ponieważ przypisanie sidebar_enabled w "index.html" następuje przed extend, to linia pobierająca wstawiona będzie przed rozpoczęciem przetwarzania "layout.html" czyniąc, że sidebar_enabled będzie dostępny w całym kodzie "layout.html" (nieco bardziej zaawansowaną wersję tego stosuje się w aplikacji welcome).

Warto również mieć na uwadze, że zmienne zwracane przez funkcje kontrolera są dostępne nie tylko w głównym widoku funkcji ale również we wszystkich jego widokach rozszerzających i wstawianych.

Argumentem dyrektyw extend lub include (tj. nazwą rozszerzonego lub wstawionego widoku) może być zmienna Pythona (lecz nie wyrażenie Pythona). Jednak nakłada to ograniczenie – widoki używające zmiennych w wyrażeniach extend lub include nie mogą być kompilowane do kodu bajtowego. Jak wspomniano powyżej, widoki skompilowane bajtowo zawierają całe drzewo widoków rozszerzających i wstawianych, tak więc określone widoki rozszerzające i wstawiane muszą być znane w czasie kompilacji, co nie jest możliwe, jeśli nazwy tych widoków są zmiennymi (ich wartości nie są określane w w czasie wykonania). Ponieważ widoki skompilowane bajtowo znacząco przyspieszają przetwarzanie, to należy unikać używania zmiennych w argumentach dyrektyw extend i include, jak to tylko możliwe.

W niektórych przypadkach, alternatywą dla używania zmiennej w dyrektywie include jest umieszczenie zwyklego wyrażenia {{include ...}} w bloku if...else.

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

Powyższy kod nie stanowi żadnego problemu dla kompilacji bajtowej, ponieważ żadne zmienne nie są zaangażowane. Należy jednak pamiętać, że skompilowany bajtowo widok w rzeczywistości zawiera kod Pythona zarówno dla "this_view.html" jak i "that_view.html", choć tylko kod jednego z nich zostanie wykonany, w zależności od wartości some_condition.

Trzeba pamiętać, że działa to tylko dla dyrektywy include -- nie można umieścić dyrektywy {{extend ...}} w bloku if...else.

response.menu
menu
response.meta
meta

Układy są wykorzystywane do hermetyzacji ujednoliconej strony (nagłówków, stopek, menu) i choć nie są one obowiązkowe, to ułatwiają pisanie i utrzymanie aplikacji. W szczególności sugerujemy pisanie układów korzystających z następujących zmiennych, które mogą być ustawione w kontrolerze. Korzystanie z tych powszechnie znanych zmiennych pomoże uczynić układy wymienialnymi:

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

Z wyjątkiem menu i files, wszystkie te ciągi znakowe i ich znaczenie powinny być oczywiste.

Zmienna response.menu jest listą trzech lub czterech krotek. Te trzy elementy to: nazwa odnośnika, logiczna reprezentacja tego, czy odnośnik jest aktywny (jest bieżącą pozycją menu) i adres URL linkowanej strony. Na przykład:

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

Czwarty element jest opcjonalnym podmenu.

Zmienna response.files jest listą plików CSS i JS, które są wymagane na stronie.

Zalecamy również użycie w sekcji head dokumentu HTML wyrażenie:

{{include 'web2py_ajax.html'}}

ponieważ wstawia to biblioteki jQuery i definiuje kilka funkcji JavaScript wstecznej kompatybilności dla efektów specjalnych i Ajax. Plik "web2py_ajax.html" wstawia w widoku znaczniki response.meta, podstawowy kod jQuery, okienko kalendarza (ang. datepicker) i wszystkie wymagane w response.files pliki CSS i JS.

Domyślny układ strony

Twitter Bootstrap

Układ "views/layout.html" dostarczany wraz ze szkieletową aplikacją welcome ma następującą strukturę (okrojoną z niektórych opcjonalnych części):

<!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'}}

  {{
  # using sidebars need to know what sidebar you want to use
  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>
  <!-- Navbar ================================================== -->
  <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">
    <!-- Masthead ================================================== -->
    <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-->

    <!-- Footer ================================================== -->
    <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>

Istnieje kilka cech tego domyślnego układu, które sprawiają, że jest on bardzo łatwy w użyciu i dostosowaniu:

  • Jest napisany w HTML5 i stosuje bibliotekę "modernizr" [modernizr] do zapewnienia kompatybilności wstecznej. Rzeczywisty układ zawiera kilka dodatkowych wyrażeń warunkowych wymaganych przez IE i zostały one tutaj pominięte dla uproszczenia.
  • Wyświetla zarówno response.title jak i response.subtitle, które mogą zostać ustawione w modelu lub kontrolerze. Jeśli nie zostaną ustawione, przyjmie jako tytuł nazwę aplikacji.
  • Wstawia w sekcji header plik web2py_ajax.html, który generuje wszystkie wyrażenia importu link i script.
  • Wykorzystuje zmodyfikowaną wersję Twitter Bootstrap dla elastycznych układów strony, które działają na urządzeniach mobilnych i reorganizują kolumny w celu dopasowania strony do małych ekranów.
  • Używa "analytics.js" do łączenia się z Google Analytics.
  • Wyrażenie {{=auth.navbar(...)}} wyświetla powitanie dla bieżącego użytkownika i odnośniki do funkcji uwierzytelniających, takich jak login, logout, register, change password itd., w zależności od kontekstu. Jest to helper fabryka i jego wyjściem można manipulować jak w każdym innym helperze. Jest on umieszczany w bloku {{try:}}...{{except:pass}} na wypadek gdy uwierzytelniania nie zostanie zdefiniowane.
  • Wyrażenie {{=MENU(response.menu)}} wyświetla strukturę menu jako <ul>...</ul>.
  • Wyrażenie {{include}} jest zamieniane przez treść rozszerzającego widoku podczas renderowania strony.
  • Domyślnie wykorzystuje warunkowy układ trzykolumnowy (lewy i prawy pasek boczny może zostać wyłączony przez widoki rozszerzające).
  • Używa klas: header, main, footer.
  • Zawiera bloki: statusbar, left_sidebar, center, right_sidebar, footer.

W widokach można włączyć i dostosować paski boczne w sposób następujący:

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

This text goes in center

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

Dostosowywanie domyślnego układu

CSS

Dostosowywanie domyślnego układu bez jego edytowania jest łatwe, ponieważ aplikacja welcome jest oparta na Twitter Bootstrap, który jest dobrze udokumentowany i obsługuje motywy. W web2py są cztery statyczne pliki istotne dla stylizacji:

  • "css/web2py.css" zawiera style specyficzne dla web2py;
  • "css/bootstrap.min.css" zawiera style CSS Twitter Bootstrap [bootstrap];
    Bootstrap
  • "css/web2py_bootstrap.css" zawiera style nadpisujące kilka stylów Bootstrap w celu dostosowania ich do potrzeb web2py;
  • "js/bootstrap.min.js" który zawiera biblioteki dla efektów menu, okien modalnych i paneli.

Jeśli chce się zmienić kolory i obraz tła, można spróbować dodać następujący kod do sekcji header pliku layout.html:

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

Oczywiście można też całkowicie wymienić pliki "layout.html" i "web2py.css" według własnych potrzeb.

Układy na urządzenia mobilne

Domyślnie layout.html jest stworzony tak, aby był przyjazny dla urządzeń mobilnych, ale to może nie wystarczyć. Może zachodzić potrzeba użycia specjalnych widoków, gdy strona jest odwiedzana z urządzenia mobilnego.

W celu wspomożenia pracy nad graficznymi interfejsami dla różnych urządzeń stacjonarnych i przenośnych, web2py oferuje dekorator @mobilize. Dekorator ten stosuje się do akcji, które mają widok dla urządzeń stacjonarnych i mobilnych. Oto przykład:

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

Dekorator musi być importowany przed wykorzystaniem go w kontrolerze. Gdy funkcja "index" jest wywoływana w zwykłej przeglądarce (na komputerze stacjonarnym), web2py będzie renderował zwracany słownik przy użyciu widoku "[controller]/index.html". Lecz gdy funkcja ta będzie wywoływana w urządzeniu mobilnym, słownik będzie renderowany przez widok "[controller]/index.mobile.html". Proszę zauważyć, że mobilny widok ma tutaj rozszerzenie "mobile.html".

Alternatywnie można zastosować następującą logikę do wykonania widoków przyjaznych dla urządzeń mobilnych:

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

Wprawdzie zadanie utworzenia widoków "*.mobile.html" leży w gestii programisty, ale zalecamy używanie wtyczki "jQuery Mobile", która czyni takie zadanie bardzo prostym.

Funkcje w widokach

Rozważmy widok "layout.html":

<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{if 'mysidebar' in globals():}}{{mysidebar()}}{{else:}}
        my default sidebar
      {{pass}}
    </div>
  </body>
</html>

i ten widok rozszerzający:

{{def mysidebar():}}
my new sidebar!!!
{{return}}
{{extend 'layout.html'}}
Hello World!!!

Powyższa funkcja jest zdefiniowana przed wyrażeniem {{extend...}} -- w efekcie funkcja zostaje utworzona zanim wykonany będzie kod "layout.html", tak więc funkcja może być wywołana w każdym miejscu "layout.html", nawet przed wyrażeniem {{include}}. Trzeba pamiętać, że funkcja jest wstawiana w rozszerzonym widoku bez przedrostka =.

Kod ten generuje następujące wyjście:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my new sidebar!!!
    </div>
  </body>
</html>

Powyższa funkcja jest zdefiniowana w HTML (choć może też zawierać kod Pythona), tak że response.write jest użyty do zapisu jej treści (funkcja nie zwraca treści). Dlatego właśnie układ wywołuje funkcję widoku używając {{mysidebar()}} zamiast {{=mysidebar()}}. Funkcje zdefiniowane w ten sposób mogą pobierać argumenty.

block

Bloki w widokach

Podstawowym sposobem ma uczynienie widoków bardziej modularnymi jest użycie wyrażeń {{block...}}. Mechanizm ten jest alternatywą dla mechanizmu omówionego w poprzednim rozdziale.

Aby zrozumieć jak to działa, rozważmy aplikacje oparte na szkielecie Welcome, który ma widok layout.html. Widok ten jest zawarty w widoku default/index.html w wyniku wyrażenia {{extend 'layout.html'}}. Treść layout.html definiuje wstępnie pewne bloki z jakąś zawartością i dlatego są one zawarte w default/index.html.

Można zastąpić domyślne treści bloków załączając nowa treść wewnątrz takiego bloku o tej samej nazwie. Położenie bloku w layout.html nie ulega zmianie, ale treść bloku tak.

Oto wersja uproszczona. Załóżmy, że mamy taki "layout.html":

<html>
  <body>
    {{include}}
    <div class="sidebar">
      {{block mysidebar}}
        my default sidebar (this content to be replaced)
      {{end}}
    </div>
  </body>
</html>

oraz takie prosty widok rozszerzający default/index.html:

{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
my new sidebar!!!
{{end}}

Generuje on następujące wyjście, gdzie treść jest dostarczana przez przesłaniający blok w widoku rozszerzającym, ale zakrywany DIV i klasa znajdują się w layout.html. Pozwala to na osiągnięcie spójności widoków:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my new sidebar!!!
    </div>
  </body>
</html>

Rzeczywisty layout.html definiuje kilka pożytecznych bloków i można łatwo dopasować układ do swojej wizji.

Można mieć wiele bloków i jeśli blok jest obecny w widoku rozszerzonym ale nie w widoku rozszerzającym, użyta zostanie treść z widoku rozszerzanego. W przeciwieństwie do funkcji, jest zbyteczne definiowanie bloku przed wyrażeniem {{extend ...}} -- nawet jeśli zdefiniowano bloki po wyrażeniu extend, mogą być one użyte do zastąpienia gdziekolwiek w rozszerzanym widoku.

super

Wewnątrz bloku można używać wyrażenia {{super}} w celu wstawienia treści widoku nadrzędnego. Na przykład, jeśli wymienimy wyżej omówiony widok rozszerzający na:

{{extend 'layout.html'}}
Hello World!!!
{{block mysidebar}}
{{super}}
my new sidebar!!!
{{end}}

otrzymamy:

<html>
  <body>
    Hello World!!!
    <div class="sidebar">
        my default sidebar
        my new sidebar!!!
    </div>
  </body>
</html>
 top