web.py – šablonovací systém

Šablonovací systém je důležitou součástí webového frameworku, proto se dnes budeme věnovat šablonovacímu systému web.py – Templatoru. Vypíšeme si hlavní konstrukce, řekneme si něco o slučování šablon a vše si předvedeme na příkladech.
Seriál: Webový framework web.py (5 dílů)
- Úvod do webového frameworku web.py 14. 11. 2013
- web.py – první aplikace 22. 11. 2013
- web.py – šablonovací systém 9. 12. 2013
- web.py – databáze 27. 12. 2013
- web.py – autentizace a autorizace 1. 9. 2014
V minulém díle jsme se věnovali základům vývoje aplikací ve frameworku web.py, především pak komponentě controlleru. V dnešním díle se budeme věnovat jiné důležité komponentě, a to pohledům (views), které zajišťují vykreslování uživatelského prostředí. Povíme si o šablonovacích systémech Pythonu, konstrukcích známých z prostého Pythonu a slučování šablon.
Templetor
Součástí web.py je šablonovací systém Templetor. Jeho syntaxe je velice podobná (takřka totožná) syntaxi Pythonu, jeho ovládnutí je tedy otázkou několika chvil. Pokud chcete raději používat šablonovací systémy jako Jinja2 nebo Cheetah, i ty lze po krátké konfiguraci ve web.py používat.
Pokud chcete Templetor v aplikaci použít, je nutné jej nejdřív nakonfigurovat. Do hlavního souboru aplikace stačí přidat render
a jako parametr mu předat složku obsahující šablony:
render = web.template.render('templates')
Funkce vykreslující šablonu může vypadat například takto, tečkovou konvencí zde určíme její umístění ve složce šablon:
return render.informace.kontakt()
Samotná šablona je soubor s koncovkou html, v němž do obyčejného HTML kódu vkládáme konstrukce Templatoru.
Proměnné
Přístup k proměnným (a funkcím) aplikace je v Templatoru standardně omezen pouze na proměnné předané při vykreslení šablony. Díky tomu je aplikace od šablon maximálně oddělena.
Pokud předáváme proměnnou aplikace šabloně:
return render.informace.kontakt("Petr", "Letovice")
je třeba na začátku šablony určit přijímané proměnné (ve správném pořadí) a jejich názvy:
$def with (jmeno, mesto)
K proměnným lze poté přistupovat hned několika způsoby:
$jmeno
${jmeno + ' ' + mesto}
$slovnik[klic].funkce('argument').
$(promenna)nanana.
Pro přiřazování hodnot do proměnných lze použít následující konstrukci:
$ promenna = (10 / 5) * 3
Templator standardně filtruje „nebezpečné“ znaky, které by mohly ovlivnit šablonu, pokud tedy necháte vypsat 1 < 2
, systém vyrendruje 1 < 2
. Pro vypnutí filtru stačí při vypisování namísto $promenna
použít $:promenna
.
Protože je znak dolaru $
využit pro syntaxi, pro vypsaní samotného znaku budete muset použít $$
.
Globální proměnné
Pokud chcete šablonám zpřístupnit nějakou proměnnou či funkci z aplikace, stačí při tvorbě renderu předat do proměnné globals
slovník.
render = web.template.render('templates', globals={'funkce':funkce_aplikace,'promenna':promenna_aplikace})
Komentáře
Pro zapsání komentářů stačí na začátek řádku přidat $#
. Tento kód se potom neobjeví ve výsledném HTML.
$# komentář kódu šablony
Větvení
Větvení kódu zde funguje takřka stejně jako v samotném Pythonu. Stačí zapsat $if
, $elif
či $else
, podmínku a za dvojtečku už samotný kód. Nezapomeňte stejně jako v Pythonu, odrážet kód bloků.
$if 1 < 2:
Jedna je menší než dvě.
$else:
Jedna je větší nebo rovno dvěma.
Kód lze psát i do jednoho řádku:
$if 1 < 2: Jedna je menší než dvě.
Cykly
Pro cyklení zde slouží $for
a $while
, zápis je podobný jako u větvení:
$for i in range(10):
Číslo $i
$while podminka:
Žluťoučký kůň úpěl.
Z objektu loop
můžete získat proměnné určující stav procházení cyklem, to se může hodit například při práci s tabulkami:
loop.index: počet projití cyklu (začíná 1)
loop.index0: počet projití cyklu (začíná 0)
loop.first: True pokud jde o první projití
loop.last: True pokud jde o poslední projití
loop.odd: True když jde o liché projití
loop.even: True když jde o sudé projití
loop.parity: vrací odd pri lichém projití a even při sudém
loop.parent: vrací vnější smyčku
Funkce
V šabloně je možné díky $def
definovat i samotné funkce:
$def pozdrav(jmeno):
Ahoj $jmeno
$pozdrav("světe")
Samotný kód
A konečně, do šablon lze pomocí $code
zapisovat čistý kód Pythonu:
$code:
x = 2
for i in range(4):
x = x * x
def funkce():
...
...
Z bezpečnostních důvodů ale není možné využívat exec
, importy, přistupovat k atributům začínajících podtržítkem _
a používat vestavěné funkce jako open
, getattr
, setattr
a podobně.
Základní šablona
Aby nebylo nutné v každé šabloně opakovat kód hlavičky, menu, patičky apod., je možné vytvořit základní šablonu, do které se budou další šablony vnořovat. Soubor základní šablony určíme při vytváření renderu pomocí atributu base
:
render = web.template.render('templates', base='base')
Základní šablona by pak mohla vypadat například takto, proměnnou content
určujeme místo, na které se má vložit obsah dalších šablon, proměnnou titulek
získanou z objektu content
využijeme pro vypsání titulku stránky:
$def with (content)
<html>
<head>
<meta charset="utf-8">
<title>$content.titulek</title>
</head>
<body>
$:content
</body>
</html>
Kód vnořené šablony by mohl vypadat takto, pomocí $var
zde definujeme proměnnou titulku, jako content
se použije zbytek šablony:
$var titulek: Nadpis stránky
<h1>Ahoj Světe</h1>
Poté už stačí jen použít použít render k vykreslení stránky.
Seznamy v proměnných
Pokud chcete vkládat celé seznamy proměnných (například adresy CSS souborů), není nutné je definovat jednotlivě. Stačí do proměnné v podšabloně zapsat seznam hodnot oddělený mezerami:
$var csssoubory: style.css fonty.css
<h1>Ahoj Světe</h1>
V hlavní šabloně pak proměnnou rozdělíme do seznamu a ten necháme pomocí cyklu vypsat:
$def with (content)
$if content.csssoubory:
$for c in content.csssoubory.split():
<link rel="stylesheet" href="$c" type="text/css"/>
Statické soubory
Protože nelze z vnějšku přistupovat k souborům aplikace, je nutné pro statické soubory (jako jsou kaskádové styly, obrázky, JavaScript) vytvořit složku static. K souborům z této složky lze poté přistupovat na adrese /static/nazev-souboru.css.
Příklad aplikace
V dnešním příkladu budeme vycházet z aplikace vytvořené minule. Upravíme úvodní stránku, která zatím pouze vrací text „Ahoj“.
Nejdříve přidejte do projektu složku templates a v ní vytvořte soubor základní šablony base.html:
$def with (content)
<html>
<head>
<meta charset="utf-8">
<title>$content.titulek</title>
<style>
body {
background-color: #DEFCD7;
}
</style>
</head>
<body>
$:content
</body>
</html>
Dále vytvořte šablonu úvodní strany uvod.html:
$var titulek: Úvodní strana
<h1>Ahoj Světe</h1>
Nyní otevřete základní soubor aplikace main.py, přidejte render a pozdrav „Ahoj“ nahraďte vykreslením šablony:
# -*- coding=utf-8 -*-
import web
urls = (
'/','Uvod',
'/umocni/(\d+)', 'Umocni',
)
app = web.application(urls, globals())
render = web.template.render('templates', base='base')
class Uvod:
def GET(self):
return render.uvod()
class Umocni:
def GET(self, cislo):
return int(cislo) * int(cislo)
if __name__ == "__main__":
app.run()
Nakonec si můžete aplikaci pustit, při otevření adresy http://localhost:8080/ by se měla zobrazit stránka s pozdravem v nadpisu.
Závěr
To je pro tento díl vše. Příště se budeme věnovat databázím, autorizaci a autentizaci uživatelů.
Nikdy jsem nepochopil, proč většina frameworků používá pro jednoduchý výpis proměnné poměrně složitou syntaxi jako Jinja {{ foo }}, Mako ${name}.
Proti tomu obyčejné $name v templatoru je geniálně jednoduché a o 2 až tři znaky kratší. (A takové PHP-intuitivní, vždyť vlastně i to PHP je (mělo být) spíš šablonovací systém, než programovací jazyk :-) )
Opravdu by mne zajímalo, co autory většiny frameworků vede k tomu volit tak složitou syntaxi?
Proc se ptas zde a nezeptas se autoru?
Důvod bych viděl v zjednodušení implementace – ať už vývojář sprcá template engine pár regulárními výrazy, nebo použije opravdový lexer a klon YACCu, když je jasně určený levý a pravý delimiter, snadněji se to parsuje. A že jsou zrovna tolik oblíbené kudrnaté závorky, je tím, že se v HTML moc nevyskytují.
Protoze nestačí jenom $var, ale je potřeba htmlspecialchars($var, ENT_QUOTES);
Jde spíš jen o syntaxi, Python se dá všelijak přiohnout.
‚
Viz třeba způsob jakým Templetor volá konkrétní šablonu
render.informace.kontakt()
namísto obvykléhorender("informace/kontakt.html")
. Konkrétně za tímto musí být „nadbytečný“ kód navíc, který pomocí getattr slepí cestu k šabloně, ale to by snad nemuselo až tak vadit, při použití dobrého cachování.Trochu jsem se chtěl na web.py podívat, ale verze pod Python 3 zlobí, respektive řekl bych, že vůbec není udělaná, jsou tam nefunkční importy. Zkoušel jsem to opravit, ale vzdávám to. Škoda, docela zajimavý framework.
Díky za další díl o web.py frameworku. Mám jen dva hnidopišské postřehy :-)
title
získanou z objektu content využijeme pro vypsání titulku stránky“ by vzhledem k dalšímu kódu měl být název proměnnétitulek
;souborycss
acsssoubory
.Ale jinak dobrá práce!