Chapter 3: Overview
Přehled
Spuštění
Web2py je pro Windows a Mac OS X připraveno v přímo spustitelných souborech. Zahrnují i interpreter Pythonu, takže není třeba jej předinstalovat. Dále je k dispozici zdrojová (source code) verze, která běží na Windows, Macu, Linuxu, a ostatních na Unixu založených systémech. Zatímco přímo spustitelné verze obsahují Python, pro zdrojovou verzi musíte Python (např. verzi 2.7x) předinstalovat.
Web2py nepotřebuje instalaci. Abyste začali, rozbalte (unzip) stažený zip soubor, určený pro váš konkrétní operační systém, a spusťte odpovídající spusstitelný soubor web2py
.
Ve Windows, spusťte*):
web2py.exe
*) nejprve potřebujete příkazové okno (cmd.exe) a přepnout se (cd.....) do adresáře, kde je umístěno Web2py; je proto jednodušší si vytvořit zástupce (pravé tlačítko myši, Nový, Zástupce) přímo pro spuštění Web2py, kde Cíl zapíšete podle místa, kam jste Web2py nakopírovali - např. když jej umístíte jako package Pythonu do C:\Python27\Lib\site-packages\web2py
(což bude vyhovovat pro spouštění sestavené i zdrojové verze), Cíl bude: C:\Python27\Lib\site-packages\web2py\web2py.exe
a "Spustit v" bude:
C:\Python27\Lib\site-packages\web2py
Na OS X, spusťte:
open web2py.app
Na Unixu a Linuxu, spusťte Web2py ze zdrojových kódů zápisem:
python web2py.py
Chcete-li i na Windows spouštět Web2py ze zdrojových kódů, nainstalujte si nejprve Mark Hammond's "Python for Windows extensions, a pak spusťte:
python web2py.py
Web2py program rozumí mnoha parametrům na příkazové řádce, o nich si povíme později.
Při implicitním spuštění Web2py zobrazí spouštěcí okno s výzvou pro zadání jednorázového administrátorského hesla a IP adresy a čísla portu, kde bude dostupný webový server. Implicitně Web2py spustí vlastní (vestavěný) webový server na 127.0.0.1:8000 (port 8000 na localhost), ale můžete jej spustit na jakékoli IP adrese a portu, které máte k dispozici. Můžete si zjistit IP adresu vašeho síťového rozhraní otevřením příkazového okna a příkazem ipconfig
ve Windows (resp. ifconfig
na OS X a v Linuxu). Zapište 0.0.0.0:80, chcete-li, aby Web2py běželo veřejně na všech síťových rozhraních, která jsou k dispozici. V dalším textu budeme předpokládat, že jste Web2py spustili na localhost (127.0.0.1:8000).
Jestliže administrátorské heslo neuvedete, administrační rozhraní bude zakázáno. To je bezpečnostní opatření, aby se zabránilo veřejnému zpřístupnění administračního rozhraní.
Administrační rozhraní (aplikace admin), je z localhost dostupná, když nespustíte Web2py se serverem Apache s mod_proxy. Když admin detekuje proxy, session cookie je nastaveno na bezpečné (secure) a přihlášení do admin nebude dostupné, pokud komunikace mezi klientem a proxy nepůjde přes HTTPS; to je opět bezpečnostní opoatření. Veškerá komunikace mezi klientem a admin aplikací musí vždy být buď lokální nebo šifrovaná; jinak by totiž útočník mohl provést útok man-in-the middle nebo replay attack a spustit podsunutý kód na serveru.
Jakmile jste zadali administrační heslo, Web2py začne obsluhovat stránku prohlížeče na adrese:
http://127.0.0.1:8000/
Prohlížeč tuto adresu zobrazí. Jestliže nemáte na svém počítači nastaven implicitní browser (prohlížeč), otevřte prohlížeč, který chcete použít, a zadejte výše uvedenou URL adresu.
Klepnutím na "administrační rozhraní" ("administrative interface") se dostanete do přihlašovací stránky (login page) administračního rozhraní.
Zadáte heslo administrátora, které jste použili při spouštění Web2py. Poznamenejme, že existuje jediný administrátor, a tedy také jediné administrátorské heslo. Z bezpečnostních důvodů si vývojář volí při každém spuštění nové heslo. Pokud si to nepřejete, můžete použít při spuštění parametr <recycle>, takže bude použito předchozí heslo.
Přihlášením se ocitnete na hlavní stránce administračního rozhraní.
Jsou zde vypsány všechny instalované Web2py aplikace a administrátor má možnost je spravovat. Web2py po instalaci obsahuje 3 aplikace:
- admin aplikace je ta, kterou právě popisujeme.
- examples aplikace obsahuje interaktivní dokumentaci a částečně duplikuje oficiální webovou stránku frameworku.
- welcome aplikace je základní šablonou pro další Web2py aplikace. Je to možná základní "kostra" vaší budoucí aplikace (ačkoli můžete také začít na čisté louce). Je to zároveň aplikace, která uživatele vítá po spuštění frameworku.
Existují další předem připravené Web2py aplikace. Můžete si stáhnout různé volně dostupné aplikace z [appliances] . Jste zváni poskytnout ostatním nové aplikace, které vytvoříte, ať už v open-source nebo closed-source (kompilované a pakované) formě.
Z hlavní stránky administračního rozhraní můžete provádět následující operace:
- install - instalovat dříve vytvořenou aplikaci pomocí formuláře v pravém dolním rohu; pojmenujte aplikaci, zvolte soubor s pakovanou aplikací nebo URL aplikace a stiskněte "submit".
- uninstall - odinstalovat aplikaci klepnutím na odpovídající tlačítko; následuje potvrzovací stránka.
- create - vytvořit novou aplikaci (zadáním jejího jména a kliknutím na "create").
- package - zabalit apkaci k distribuci klepnutím na odpovídající tlačítko. Pakovaná (sbalená) aplikace je tar soubor, který obsahuje vše, včetně databáze. Při opětovném použití zabalené aplikace není třeba tento soubor rozbalit (untar). To udělá automaticky instalace aplikace pomocí administračního rozhraní (jak jsme právě popsali).
- clean up - odstraní dočasné soubory, vzniklé provozem aplikace: sessiony, chybové tikety a cache.
- edit - editace (vývoj) aplikace.
Když vytvoříte novou aplikaci pomocí admin rozhraní, vznikne jako kopie "kostry", tj. výše popsané "welcome" aplikace. Obsahuje soubor "models/db.py", který vytvoří SQLite databázi, připojí se k ní, instanciuje a konfiguruje hlavní objekty Auth, Crud a Service. Dále obsahuje soubor "controller/default.py", která zveřejní (exposes) akce "index", "download", "user" (pro správu uživatelů), a "call" (pro poskytování webových služeb). Můžete ale tyto soubory smazat. V následujícím budeme předpokládat, že uvedené soubory jsou smazány a ukážeme si, jak vytvořit aplikaci na zelené louce (from scratch).
Kromě standardního začátku vývoje aplikace (kopie Welcome aplikace) nebo vývoje od počátku (from scratch) můžete ještě využít wizard, který bude popsán později, který vytvoří alternativní variantu "kostry" (základního kódu) za pomoci vzhledů (layouts) a pluginů, dostupných na webu.
"Hello" aplikace
Nejprve jako příklad si vytvoříme nejjednodušší aplikaci, která zobrazí hlášení "Hello from MyApp". Pojmenujeme si ji třeba "myapp". Přidáme si také čítač, který napočítá, kolikrát uživatel tuto stránku zobrazil.
Jak už bylo řečeno, novou aplikaci založíme zápisem jejího jména v pravém sloupci a klepnutím na create (vytvoř).
Po stisknutí [create] se aplikace vytvoří jako kopie originální Welcome aplikace.
Je možné ji hned spouštět - vyzkoušejte:
http://127.0.0.1:8000/myapp
Zatím máme jen přesnou kopii Welcome aplikace.
Pro editaci (vývoj) klepněte na edit tlačítko u aplikace MyApp.
edit stránka zobrazí, co všechno je součástí aplikace. Každá Web2py aplikace je tvořena určitými soubory, z nichž většina spadá do některé z těchto kategorií:
- models: popisují strukturu databáze.
- controllers: popisují aplikační logiku a workflow (postup činnosti).
- views (pohledy): popisují prezentaci dat.
- languages: překladové tabulky pro provoz aplikace v dalších jazycích.
- modules: Python moduly lokální pro aplikaci.
- static files (statické soubory): obrázky, CSS soubory [css:w,css:o,css:school] , JavaScript [js:w,js:b] , apod.
- plugins: skupiny souborů, připravených aby vzájemně spolupracovaly, a použitelných pro různé aplikace.
Vše je uspořádáno podle návrhového vzoru Model-View-Controller. Každá sekce edit stránky odpovídá složce (adresáři) ve složce aplikace na disku.
Poznamenejme, že klepnutím na hlavičku sekce rozvinete nebo svinete její položky. Stejně se také chovají jména složek v sekci Static files.
Každý soubor, zobrazený v některé sekci, fyzicky odpovídá souboru, který je umístěn na disku v příslušném podadresáři. Neboli každá operace se souborem, provedená z admin rozhraní (create, edit, delete) může být provedena i mimo admin rozhraní ze shellu (příkazového okna), případně za pomoci vámi preferovaného editoru.
Aplikace obsahuje ještě další soubory (databázi, session soubory, sooubory chybových tiketů, apod.), ale ty se nevypisují v edit stránce, protože je nevytváří ani neupravuje vývojář, nýbrž aplikace samotná.
Controllery obsahují logiku a workflow aplikace. Každé URL je namapováno na volání některé funkce v controlleru (na některou akci). Po vytvoření aplikace implicitně existují controllery: "appadmin.py" a "default.py". appadmin poskytuje administrační rozhraní k databázi naší aplikace; nyní se ním nebudeme zabývat. "default.py" je controller, který potřebujete modifikovat, aby vaše aplikace dělala to, co potřebujete. "default.py" controller je použit, není-li v URL (webové adrese) explicitně uveden některý jiný. Upravte "index" funkci v "default.py" následujícím způsobem:
def index():
return "Hello from MyApp"
Takto to bude vypadat (když nepoužijeme externí editor, ale vestavěný online editor):
Uložte změny a vraťte se do edit stránky. Klepněte na odkaz "index" v seznamu akcí, které zveřejňuje controller "default.py". Tím přejdete na nově vytvořenou stránku.
Navigujete-li v prohlížeči na odkaz (tuto adresu také můžete vidět v prohlížeči při najetí na odkaz "index"):
http://127.0.0.1:8000/myapp/default/index
zavolá se z "default.py" controlleru akce "index". Ta vrátí řetězec, který nám browser promítne. Mělo by to vypadat takto:
A teď upravte (edit) "index" funkci následujícím způsobem:
def index():
return dict(message="Hello from MyApp")
Jestliže funkce nevrátí řetězec, ale dictionary, bude se na zpracování podílet ještě html šablona (view neboli template). Editujte tedy ještě view "default/index.html" (což je view, které je automaticky asociováno s akcí "index") a zcela nahraďte dosavadní obsah souboru takto:
<html>
<head></head>
<body>
<h1>{{=message}}</h1>
</body>
</html>
Nyní tedy akce (funkce controlleru) vrací dictionary, jejímž jedním (a jediným) klíčem je message
. Když akce vrátí dictionary, Web2py automaticky hledá view se jménem
[controller]/[function].[extension]
a vykoná ho. [extension] znamená požadovanou příponu souboru. Není-li uvedena žádná, implicitně se dosadí přípona ".html" - a právě to je náš případ. Pro view v tomto případě sice používáme ".html" příponu, ale není to přímo ".html" soubor. Je to šablona, která volá i kód v Pythonu, zapsaný pomocí speciálních značek {{ }}. V našem příkladu {{=message}}
říká Web2py, aby do tohoto místa v šabloně vypsalo hodnotu message
(položku dictionary, vrácené jako výsledek akce, s klíčem message
).
Kdyby Web2py nenalezlo view (šablonu) se jménem [controller]/[function].[extension], použije "generic.html" view, které je součástí každé aplikace.
Mac MailGoogle MapsjsonpJe-li uvedena jiná přípona než "html" (např. "json"), a neexistuje-li soubor "[controller]/[function].json", Web2py obdobně použije "generic.json". Web2py má k dipozici tyto implicitní šablony: generic.html, generic.json, generic.jsonp, generic.xml, generic.rss, generic.ics (pro Mac Mail Calendar), generic.map (pro vložení Google Maps), a generic.pdf (založenou na fpdf). Tato generická view mohou být upravena i pro jednotlivé aplikace individuálně a není také problém přidávat další nové formáty view.
Na generická (implicitní) view nahlížejme jako na vývojový nástroj. Během vývoje bychom měli pro každou akci vytvořit její vlastní view. Ve skutečnosti dokonce generická view nejsou defaultně vůbec povolena v produkčním prostředí, ale pouze na localhost.
Můžete také v akci controlleru vynutit jakékoli jiné view pomocí
response.view = 'default/something.html'
Více si o tom můžete přečíst v kapitole 10.
Jestliže se vrátíte do "EDIT" seznamu souborů a klepnete na odkaz "index", uvidíte nyní tuto HTML stránku:
Pro ladící účely si do view můžete kdykoli připsat
{{=response.toolbar()}}
a na výsledné webové stránce se vám zobrazí užitečné informace, včetně objektů request, response a session, a také seznam všech provedených dotazů do databáze včetně jejich doby provedení.
Počítejme..
Nyní si do stránky přidejme čítač, který spočítá, kolikrát si návštěník tuto stránku zobrazil.
Web2py automaticky a transparentně sleduje návštěvy pomocí sessions a cookies. Pro každého nového návštěvníka vytvoří session a přidělí unikátní "session_id". Session je kontejner pro proměnné, které se ukládají na straně serveru. Unikátní session id je odesláno browseru pomocí cookie. Až si návštěvník vyžádá další stránku ze serveru, prohlížeč pošle cookie zpět. Web2py pomocí takto získaného id obnoví odpovídající session tohoto uživatele.
Použijeme tedy session a upravíme si default controller takto:
def index():
if not session.counter:
session.counter = 1
else:
session.counter += 1
return dict(message="Hello from MyApp", counter=session.counter)
Poznamenejme, že message
ani counter
nejsou klíčová slova Web2py, ale session
je. Ptáme se Web2py, zda existuje counter proměnná v objektu session, a když neexistuje, založíme ji a inicializujeme na 1. Kdežto když už existuje, inkremetujeme hodnotu čítače. A nakonec hodnotu čítače předáme jako další klíč (pojmenovaný counter) v dictionary, které předáváme do view.
V Pythonu to lze napsat i kompaktněji:
def index():
session.counter = (session.counter or 0) + 1
return dict(message="Hello from MyApp", counter=session.counter)
Nyní si ještě upravíme view, aby nově předanou hodnotu používalo a zobrazovalo:
<html>
<head></head>
<body>
<h1>{{=message}}</h1>
<h2>Počet návštěv: {{=counter}}</h2>
</body>
</html>
Budete-li stránku navštěvovat opakovaně (třeba pomocí Refresh), budete dostávat následující HTML stránku:
Čítač je (díky jedinečnosti session id a session objektu pro jednotlivé návštěvníky) asociován ke konkrétnímu návštěvníkovi a zvyšován o +1 vždy, když návštěvník znova načte stránku. Různí návštěvníci tedy vidí každý svůj počet návštěv.
Napiš mé jméno..
Nyní zkusme vytvořit 2 stránky (první a druhou), kde první stránka poskytne formulář, zeptá se návštěvníka na jméno, a přesměruje pak na druhou stránku, která návštěvníka pozdraví jeho jménem.
Do default.py controlleru zapište následující akce:
def first():
return dict()
def second():
return dict()
Pak vytvořte view (nový soubor šablony) "default/first.html" pro první akci, a zadejte:
{{extend 'layout.html'}}
Jak se jmenujete?
<form action="second">
<input name="visitor_name" />
<input type="submit" />
</form>
Teď ještě vytvořte view "default/second.html" pro druhou akci:
{{extend 'layout.html'}}
<h1>Hele {{=request.vars.visitor_name}}, ahoj!</h1>
V obou view rozšiřujeme základní "layout.html" šablonu (view), která je dodána jako součást Web2py. layout.html view dá oběma stránkám jednotný rámec (jednotný vzhled). layout.html soubor, nejste-li spokojeni s dodaným souborem, si můžete jednoduše vyeditovat, ať už pro jednu aplikaci nebo jej pak kopírovat i k dalším aplikacím. Je to jednoduché - obsahuje převážně HTML kód.
Když nyní navštívíte první stránku (klepnutím na odkaz first nebo přechodem na localhost:8000/default/first), zapište své jméno:
a když potvrdíte (submit) formulář, dostanete pozdrav:
Postbacky
Tento mechanismus potvrzování formulářů je sice často používán, ale není to dobrá programátorská praktika. Veškerý vstup totiž má být validován a při uvedeném řešení by validace byla ponechána na akci "second". Neboli akce pro validaci by byla odlišná od akce pro vystavení formuláře, a to by svádělo k redundanci (zbytečnému a destabilizujícímu opakování) kódu.
Lepší postup pro potvrzení (submit) formuláře je potvrzením vyvolat tutéž akci, která formulář sestavila, v našem případě akci "first". "first" akce dostane proměnné, zpracuje je, uloží je na straně serveru, a pak přesměruje návštěvníka na "second" stránku, která si proměnné na serveru načte. Tento meechanismus se nazývá postback.
Upravíme si default controller, aby implementoval samo-potvrzení (self-submission):
def first():
if request.vars.visitor_name:
session.visitor_name = request.vars.visitor_name
redirect(URL('second'))
return dict()
def second():
return dict()
Upravíme "default/first.html" view:
{{extend 'layout.html'}}
Jak se jmenujete?
<form>
<input name="visitor_name" />
<input type="submit" />
</form>
a "default/second.html" view si musí data vzít ze session
objektu, místo z request
objektu (místo z request.vars
):
{{extend 'layout.html'}}
<h1>Hele {{=session.visitor_name or "anonym"}}, ahoj!</h1>
Z pohledu návštěvníka se samo-potvrzení chová přesně stejně jako předchozí implementace. Ještě jsme nepřidali validaci, ale nyní je jasné, že validaci bude zajišťovat akce "first".
Tento přístup je lepší i proto, že jméno návštěvníka zůstane v session-ě a může pak být využíváno všemi akcemi a view aplikace, aniž by bylo potřeba jej těmto akcím a šablonám předávat explicitně.
Všimněme si, že bude-li "second" akce vyvolána dříve než "first" akce, vypíše "Hele anonym, ahoj!" protože session.visitor_name
vrátí None
. Mohli bychom to ošetřit třeba přidáním následujícího kódu do controlleru (do second
funkce):
if not request.function=='first' and not session.visitor_name:
redirect(URL('first'))
To je obecný mechanismus, jak zajistit autorizaci v controllerech, ale v kapitole 9 se podíváme na rychlejší způsoby.
S Web2py můžeme jít o krok dále a nechat Web2py, aby vytvořilo formulář za nás, včetně validace. Web2py poskytuje helpery (FORM, INPUT, TEXTAREA, a SELECT/OPTION) se shodnými jmény jako mají jejich ekvivalentní HTML tagy. Lze je použít pro sestavení formuláře, ať už v controlleru nebo ve view.
Např. tady je možný způsob, jak přepsat "first" akci:
def first():
form = FORM(INPUT(_name='visitor_name', requires=IS_NOT_EMPTY()),
INPUT(_type='submit'))
if form.process().accepted:
session.visitor_name = form.vars.visitor_name
redirect(URL('second'))
return dict(form=form)
kde říkáme, že FORM tag bude obsahovat 2 INPUT tagy. Atributy INPUT tagů můžeme specifikovat pomocí stejnojmenných argumentů, před které přidáme podtržítko. requires
argument nezačíná podtržítkem, proto se z něj nestane atribut tagu INPUT. Místo toho zavádí validaci na zadání jména návštěvníka (visitor_name).
Tady je další, ještě lepší způsob, jak vytvořit identický formulář:
def first():
form = SQLFORM.factory(Field('visitor_name', requires=IS_NOT_EMPTY()))
if form.process().accepted:
session.visitor_name = form.vars.visitor_name
redirect(URL('second'))
return dict(form=form)
Zde nedefinujeme tagy, ale jen pole fiktivní databázové tabulky (v našem případě jediné pole "visitor_name"). form
objekt lze ve view velice snadno serializovat. Takto jej vložíme do "default/first.html" view.
{{extend 'layout.html'}}
Jak se jmenujete?
{{=form}}
form.process()
metoda aplikuje validátory a vrátí opět objekt "form". form.accepted
proměnná je nastavena True poté, kdy formulář byl zpracován a údaje vyhověly validacím. Když samo-potvrzovací formulář splní validace, uloží své proměnné do session a přesměruje na druhou akci, jak jsme už popsali výše. Když validace selžou, do formuláře budou přidána chybová hlášení a zobrazena uživateli:
V další části si ukážeme, jak mohou být formuláře generovány automaticky z modelu (z popisu struktury databáze).
Obrázkový blog
Nyní, jako další příklad, bychom si chtěli vytvořit webovou aplikaci, která administrátorovi umožní zveřejňovat obrázky a pojmenovávat je, a návštěvníkům webové stránky umožní prohlížet si pojmenované obrázky a psát k nim komentáře.
Stejně jako předtím, z hlavní stránky administračního rozhraní vytvořte novou aplikaci, nazvanou images
, a přejděte na její edit stránku:
Začneme vytvořením modelu, tj. reprezentace dat aplikace (uploadované obrázky, jejich jména, a komentáře k nim). Nejprve potřebujete vytvořit a editovat model, který pojmenujeme např. "db.py". Níže uvedeným kódem přepišme případný existující kód v "db.py". Modely a controllery musí mít příponu .py
, protože se jedná o kód v Pythonu. Pokud byste příponu neuvedli, přidá ji automaticky Web2py. Na rozdíl od toho pro Views používáme .html
příponu, protože z větší části obsahují HTML kód.
Editujte "db.py" soubor klepnutím na odpovídající "edit" tlačítko:
a zapište následující:
db = DAL("sqlite://storage.sqlite")
db.define_table('image',
Field('title', unique=True),
Field('file', 'upload'),
format = '%(title)s')
db.define_table('comment',
Field('image_id', 'reference image'),
Field('author'),
Field('email'),
Field('body', 'text'))
db.image.title.requires = IS_NOT_IN_DB(db, db.image.title)
db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')
db.comment.author.requires = IS_NOT_EMPTY()
db.comment.email.requires = IS_EMAIL()
db.comment.body.requires = IS_NOT_EMPTY()
db.comment.image_id.writable = db.comment.image_id.readable = False
Vysvětlíme si to řádek po řádku.
Řádek 1 definuje globální proměnnou, nazvanou db
, která představuje připojení do databáze. V našem případě jde o připojení do SQLite databáze, uložené v jediném soubor "applications/images/databases/storage.sqlite". Jestliže pracujeme s SQLite a databáze zatím neexistuje, vytvoří se automaticky. Můžete změnit jméno souboru a zrovna tak i jméno globální proměnné, ale raději budeme používat jméno db
, aby se nám to lépe pamatovalo.
Řádky 3-5 definují tabulku "image". define_table
je metoda db
objektu. První argument, "image", je jméno tabulky, kterou definujeme. Další argumenty jsou pole tabulky. Tato tabulka má pole "title", pole "file", a pole "id", které slouží jako primární klíč ("id" není explicitně deklarováno, protože každá tabulka bude mít id pole automaticky). Pole "title" je řetězec, pole "file" je typu "upload". "upload" je speciální typ pole, které Datová Abstrakční Vrstva (Data Abstraction Layer, DAL) ve Web2py používá pro ukládání jmen uploadovaných souborů. Web2py ví, jak uploadovat soubory (za pomoci streamingu, když jsou moc veliké), jak je kvůli bezpečnosti přejmenovat a jak je ukládat.
Pro tabulku, kterou definujeme, Web2py udělá jednu z těchto akcí:
- když tabulka zaatím ještě neexistuje, vytvoří ji;
- když tabulka existuje a její struktura neodpovídá definici, je tabulka změněna na strukturu podle definice; jestliže se mění typ pole, Web2py se pokusí převést jeho obsah;
- jestliže tabulka existuje a její struktura se shoduje s definicí, Web2py neudělá nic.
Tomuto chování říkáme "migrace". Ve Web2py migrace jsou automatické, ale mohou být zakázány pro kteroukoli tabulku pomocí migrate=False
, které uvedeme jako poslední argument define_table
metody.
Řádek 6 definuje formátovací řetězec této tabulky. Ten určuje, jak bude konkrétní jednotlivý záznam reprezentován jako jediný řetězec. Poznamenejme, že format
argument může být i funkce, jejímž vstupním parametrem je záznam a která vrací řetězec. Například:
format=lambda row: row.title
Řádky 8-12 definují další tabulku, pojmenovanou "comment". Komentář má autora ("author"), "email" (chcceme zaznamenávat emailovou adresu autora komentáře), tělo ("body") typu "text" (sem budeme zaznamenávat celý text postovaný autorem), a "image_id" typu reference, které odkazuje do db.image
tabulky (na její odpovídající "id" pole).
Na řádku 14 db.image.title
reprezentuje pole "title" tabulky "image". Atribut requires
vám umožňuje zadat požadavky (omezení, constraints), která budou vynucena při zadání údaje do Web2py formulářů. Zde požadujeme, aby "title" byl vždy unikátní:
IS_NOT_IN_DB(db, db.image.title)
Poznamenejme, že to je volitelné, protože to automaticky nastavuje už definice pole: Field('title', unique=True)
.
Objekty, které definují tato omezení, se nazývají validátory. Více validátorů může být seskupeno do listu (seznamu). V takovém případě se validátory vyhodnocují v pořadí, v jakém byly zadány. IS_NOT_IN_DB(a, b)
je speciální validátor, který zkontroluje, že právě zadaná hodnota pole b
dosud není v a
.
Řádek 15 vyžaduje, aby údaj v poli "image_id" tabulky "comment" už byl v db.image.id
. Toto rovněž bude zajišťovat už i samotná definice pole "image_id" (typ reference). Nyní ale explicitně zadáváme, aby Web2py vyžadovalo toto omezení na úrovni zpracování formulářů, když je poslán nový komentář, takže žádná nesprávná hodnota se nemůže se vstupního formuláře dostat do databáze. Také zde určujeme, že místo "image_id" se bude zobrazovat "title", '%(title)s'
, tj. titulek příslušného obrázku.
Řádek 20 zabraňuje tomu, aby se pole "image_id" tabulky "comment" nezobrazovalo ve formulářích (writable=False
) ani v readonly formulářích (readable=False
).
Význam validátorů v řádcích 15-17 by měl být jasný.
Poznamenejme, že validátor
db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')
může být (a také automaticky bude) vynechán, když zadáme formát tabulky, do níž odkaz směřuje:
db.define_table('image', ..., format='%(title)s')
kde format může být řetězec a nebo funkce, která vezme jako parametr konkrétní záznam a vrátí řetězec.
Jakmile máme definován model a když v něm nejsou žádné chyby, Web2py vytvoří aplikační administrační rozhraní, kterým můžeme databázi spravovat. K němu se dostanete pomocí odkazu "database administration" v edit stránce, a nebo přímo:
http://127.0.0.1:8000/images/appadmin
Tady je screenshot appadmin rozhraní:
Rozhraní je naprogramováno v controlleru, zvaném "appadmin.py" a v odpovídajícím view "appadmin.html". Od teď budeme tomuto rozhraní jednoduše říkat appadmin. Umožňuje administrátorovi vkládat nové záznamy do tabulek, editovat a rušit existující záznamy, procházet tabulky, i spojovat tabulky (vytvářet database joins).
Když je appadmin použit poprvé, vykoná se model a v něm definované tabulky se vytvoří. DAL (Databázová Abstrakční Vrstva) přeloží kód Pythonu do SQL příkazů v dialektu právě používaného databázového stroje (SQLite v tomto případě). Generovaný SQL si můžete prohlédnout z edit stránky, klepnutím na odkaz "sql.log" v sekci "models". Poznamenejme, že tento odkaz není dostupný, dokud ještě tabulky nejsou vytvořeny.
Vyeditujete-li model a znova použijete appadmin, Web2py vygeneruje SQL příkazy ke změně struktur tabulek. Generované SQL příkazy jsou opět logovány do "sql.log".
Nyní jděte do appadmin a pokuste se vložit nový obrázek - nový záznam do tabulky image:
Web2py převedlo db.image.file
pole typu "upload" na upload formulář pro pole "file". Jakmile povtrdíte formulář a image soubor je uploadován, soubor je bezpečným způsobem přejmenován se zachováním dosavadní přípony, je uložen pod novým jménem do "uploads" složky aplikace, zatímco nové jméno je uloženo v poli db.image.file
. Tento postup je použit, aby bránil directory traversal útokům.
Poznamenejme, že k renderování (prezentaci ve formuláři) každého typu pole je použit widget. Implicitní widgety mohou být nahrazeny explicitními.
Když klepnete na jméno tabulky v appadmin, Web2py provede výběr (select) všech záznamů této tabulky, které jsou adresovány DAL dotazem:
db.image.id > 0
a vypíše výsledek.
Můžete vybrat jinou sadu záznamů tak, že budete editovat SQL dotaz a pak stisknete [Submit].
Pro editování nebo zrušení jednotlivých záznamů, klepněte na číslo záznamu (record id).
IS_IN_DB
validátor způsobuje, že odkazové pole (cizí klíč) "image_id" je renderován pomocí drop-down menu. Položky drop-down menu jsou ukládány jako klíče (db.image.id
), ale zobrazovány jsou jako db.image.title
, jak bylo ve validátoru zadáno.
Validátory jsou výkonné objekty, které umí reprezentovat pole, filtrovat hodnoty v poli, generovat chyby a formátovat hodnoty, získané z polí tabulek.
Následující obrázek ukazuje, co se stane, když potvrdíte formulář, jehož údaje nevyhoví validaci:
Stejný formulář, jaký automaticky vygeneroval appadmin, může být také vyvolán programově pomocí SQLFORM
helperu a tak použit jako součást uživateské aplikace. Tyto formuláře mohou spolupracovat s CSS a mohou být podle požadavků uzpůsobeny.
Každá aplikace má svůj vlastní appadmin; takže kdybyste modifikovali appadmin, neovlivníte ostatní aplikace.
Ukázali jsme si, jak aplikace umí ukládat data, a poznali jsme, jak můžeme k databázi přistupovat pomocí appadmin. Přístup do appadmin je omezen na administrátora, a není zamýšlen jako produkční webové prostředí pro aplkaci; proto pokračujeme v našem popisu, kde si potřebné rozhraní vytvoříme. Konkrétně chceme vytvořit:
- "index" stránku, která vypisuje všechny dostupné obrázky, setříděné podle titulku; odkazy jednotlivých obrázků vedou na detailní stránku obrázku.
- "show/[id]" stránku, která promítne návštěvníkovi požadovaný obrázek a dovolí mu číst a posílat k němu vztažené komentáře.
- "download/[name]" akci ke stahování uploadovaných obrázků.
To je schématicky reprezentováno zde:
Jděte zpátky do edit stránky a editujte "default.py" controller tak, že jeho obsah nahradíte následujícím:
def index():
images = db().select(db.image.ALL, orderby=db.image.title)
return dict(images=images)
Tato akce vrátí dictionary. Klíče položek v dictionary jsou interpretovány jako proměnné, které budou předány do view, asociovaného s touto akcí. Během vývoje, nemáme-li konkrétní view zatím vytvořeno, je výsledek akce renderován pomocí "generic.html" view, které je součástí každé aplikace.
Akce "index" provede výběr všech polí (db.image.ALL
) z tabulky "image", setříděných podle db.image.title
. Výsledkem výběru bude Rows
objekt, který obsahuje výsledné záznamy. Přiřadíme ho do lokální proměnné, nazvané images
, kterou akcce controlleru předá do view. images
je iterovatelná, jejími jednotlivými prvky jsou jednotlivé řádky (záznamy) tabulky. Na každém řádku k jeho sloupcům můžeme přistupovat jako k dictionaries: images[0]['title']
nebo ekvivalentně images[0].title
.
Jak už jsme zmínili, dokud nenapíšete view, výsledná předaná dictionary bude renderována pomocí "views/generic.html" a volání "index" akce bude vypadat takto:
Protože jste ještě nevytvořili šablonu (view), jak výsledky akce vypsat, Web2py vyrenderuje sadu záznamů formou jednoduché tabulky.
Nyní tedy pokračujme vytvořením view pro akci "index". Vraťte se do admin rozhraní, editujte "default/index.html" a nahraďte původní obsah následujícím:
{{extend 'layout.html'}}
<h1>Přehled obrázků</h1>
<ul>
{{for image in images:}}
{{=LI(A(image.title, _href=URL("show", args=image.id)))}}
{{pass}}
</ul>
Nejprve si povšimněte, že view je čisté HTML, doplněné speciálními {{...}} značkami. Kód, vložený do {{...}} je čistý Python, s jednou odlišností: Odsazování zde není relevantní a neznamená ukončení bloku (cyklu, podmínky,..). Bloky kódu zde začínají řádkem, který je ukončen dvojtečkou (:), ale ukončují se ne snížením odsazení, ale příkazem pass
. Pokud je ukončení bloku z kontextu nezaměnitelně jasné, je dokonce možné pass
vynechat.
Řádky 5-7 jdou přes všechny řádky tabulky "image" a pro každý řádek vloží toto:
LI(A(image.title, _href=URL('show', args=image.id))
Seznam, tvořený <li>...</li>
tagy, obsahuje <a href="...">...</a>
značky s textem image.title
a s hypertextovým odkazem (atribut href):
URL('show', args=image.id)
čili URL směřuje do stejné aplikace a controlleru jako aktuální request (požadavek), kde volá akci "show", a předává jí jediný argument args=image.id
. LI
, A
, apod. jsou Web2py helpers (pomocníci pro sestavení HTML), které se rozvinou na odpovídající stejnojmenné HTML tagy. Jejich nepojmenované argumenty se zpracují jako objekty, které budou serializovány a vloženy do innerHTML odpovídajího tagu. Pojmenované argumenty s podtržítkem na začátku (např. _href
) budou použity jako stejnojmenné atributy tagu (úvodní podtržítko bude vynecháno). Např. z _href
vznikne href
atribut, z _class
vznikne class
atribut, apod.
Takže třeba následující příkaz:
{{=LI(A('something', _href=URL('show', args=123))}}
bude renderován takto:
<li><a href="/images/default/show/123">something</a></li>
Praktičnost helperů (INPUT
, TEXTAREA
, OPTION
a SELECT
) dále zvyšují některé speciální pojmenované parametry, které podtržítkem nezačínají a tudíž se nepřevedou na atributy tagu (value
a requires
). Jsou důležité při tvorbě uživatelských formulářů a bude o nich řeč později.
Vraťte se nyní do edit stránky. Nyní je zobrazeno, že "default.py zveřejňuje akci "index". Klepnutím na "index" můžete navštívit nově vytvořenou stránku:
http://127.0.0.1:8000/images/default/index
Vypadá takto:
Jestliže návštěvník klikne na odkaz se jménem obrázku, bude přesměrován na adresu:
http://127.0.0.1:8000/images/default/show/1
což ale skončí chybou, protože jste zatím nevytvořil(a) akci (funkci controlleru "default.py"), pojmenovanou "show".
Editujme tedy "default.py" controller a nahraďme obsah takto:
def index():
images = db().select(db.image.ALL, orderby=db.image.title)
return dict(images=images)
def show():
image = db(db.image.id==request.args(0,cast=int)).select().first()
db.comment.image_id.default = image.id
form = SQLFORM(db.comment)
if form.process().accepted:
response.flash = 'váš komentář byl přijat'
comments = db(db.comment.image_id==image.id).select()
return dict(image=image, comments=comments, form=form)
def download():
return response.download(request, db)
Přibyly dvě akce controlleru: "show" a "download". "show" akce vybere obrázek, identifikovaný pomocí klíče id
, který získáme parsováním (rozebráním) argumentů adresy požadavku (requestu), a dále nalezne všechny komentáře, které jsou k tomuto obrázku připsaány. "show" akce pak toto vše předá do view "default/show.html".
id obrázku (záznamu v tabulce "image"), které je v "default/index.html" odkazováno pomocí:
URL('show', args=image.id)
získáme v "show" akci pomocí:
request.args(0,cast=int)
cast=int
argument je nepovinný, ale důležitý. Pokusí se převést řetězcovou hodnotu z PATH_INFO na integer. V případě chyby vyvolá správnou výjimku, místo aby vznikl chybový tiket. Lze také zadat přesměrování pro případ chyby:
request.args(0,cast=int,otherwise=URL('error'))
"download" akce očekává jméno souboru z request.args(0)
, sestaví cestu k umístění, kde se soubor nachází, a odešle soubor zpět do klienta. Je-li soubor příliš velký, streamuje jej, aby nedošlo k vyčerpání paměti.
Nyní k dalším příkazům:
- Řádek 7 nastaví hodnotu cizího klíče (odkazu na aktuální obrázek).
- Řádek 8 vytvoří formulář pro přidání záznamu SQLFORM pro
db.comment
tabulku. - Řádek 9 zpracuje potvrzený formulář (přičemž potvrzené hodnoty z formuláře jsou v
request.vars
) s ohledem na aktuální session (session je použitá pro zabránění dvojího potvrzení a pro správnou funkci navigace). Jestliže jsou potvrzené proměnné z formuláře úspěšně validovány, je nový komentář vložen dodb.comment
tabulky; v opačném případě je formulář modifikován přidáním chybových hlášení (např. není-li korektní e-mailová adresa autora). To vše zajišťuje příkaz na řádku 9!. - Řádek 10 bude prooveden jen v případě, že formulář byl potvrzen a hodnoty z něj úspěšně vylidovány, poté, co byl záznam vložen do databázové tabulky.
response.flash
je Web2py proměnná, která se zobrazuje ve views a používá pro informaci návštěvníkovi, že se něco přihodilo. - Řádek 11 vybírá všechny komentáře, které se vztahují ke konkrétnímu obrázku.
"download" akce je definována už v "default.py" controlleru vzorové aplikace (Welcome).
"download" akce nevrací dictionary, a proto nepotřebuje ani view. Naproti tomu "show" akce vrací dictionary a proto potřebuje view. Vraťte se tedy do admin rozhraní a vytvořte nové view s názvem "default/show.html".
Editujte tento nový soubor a nahraďte jeho obsah takto:
{{extend 'layout.html'}}
<h1>Obrázek: {{=image.title}}</h1>
<center>
<img width="200px"
src="{{=URL('download', args=image.file)}}" />
</center>
{{if len(comments):}}
<h2>Komentáře</h2><br /><p>
{{for comment in comments:}}
<p>{{=comment.author}} napsal(a) <i>{{=comment.body}}</i></p>
{{pass}}</p>
{{else:}}
<h2>Komentáře zatím chybí.</h2>
{{pass}}
<h2>Napiš komentář</h2>
{{=form}}
Toto view zobrazí image.file tak, že volá "download" akci uvnitř <img ... />
tagu. Jestliže obrázek jjiž byl komentován, projde všechny komentáře ve smyčce a každý z nich zobrazí.
Tady vidíme, jak se vše zobrazí návštěvníkovi:
Když návštěvník potvrdí komentář pomocí této stránky, nový komentář se uloží do databáze a přidá na konec stránky.
Přidání CRUD
Web2py také poskytuje CRUD (Create/Read/Update/Delete - Vytvoř/Čti/Edituj/Zruš) API rozhraní, které zjednodušuje formuláře dokonce ještě více. Aby bylo možné CRUD použít, je třeba jej kdekoli definovat, např. v souboru "db.py":
from gluon.tools import Crud
crud = Crud(db)
Tyto 2 řádky už jsou ve vzorové aplikaci (rozuměj v kostře aplikace, získané kopií Welcome aplikace).
crud
objekt poskytuje vysokoúrovňové metody, např.:
form = crud.create(table)
čímž lze nahradit tuto programovou strukturu:
form = SQLFORM(table)
if form.process().accepted:
session.flash = '...'
redirect('...')
Takto můžeme přepsat "show" akci s použitím CRUD, a současně uděláme některá další vylepšení:
def show():
image = db.image(request.args(0,cast=int)) or redirect(URL('index'))
db.comment.image_id.default = image.id
form = crud.create(db.comment,
message='váš komentář byl přijat',
next=URL(args=image.id))
comments = db(db.comment.image_id==image.id).select()
return dict(image=image, comments=comments, form=form)
Nejprve poznamenejme, že jsme použili syntaxi:
db.image(request.args(0,cast=int)) or redirect(...)
k získání požadovaného záznamu. Protože table(id)
vrátí None, když záznam nebude nalezen, můžeme použít or redirect(...)
na stejné příkazové řádce.
next
argument volání crud.create
je URL, kam bude návštěvník přesměrován poté, kdy hodnoty z formuláře budou akceptovány. message
argument je hlášení, které bude zobrazeno po akceptování hodnot. O CRUD se dozvíte více v kapitole 7.
Přidání Autentikace
Web2py API pro RBAC (Role-Based Access Control, Řízení přístupu pomocí rolí) je celkem sofistikované, ale nyní se omezíme jen na omezení přístupu do "show" akce na autentikované uživatele, a více ponecháme až k podrobné diskuzi v kapitole 9.
Abychom oomezili přístup jen pro oprávněné uživatele, musíme udělat 3 kroky. V modelu, např. v "db.py", musíme přidat:
from gluon.tools import Auth
auth = Auth(db)
auth.define_tables()
V controlleru potřebujeme přidat další akci:
def user():
return dict(form=auth())
To stačí k tomu, aby byly zpřístupněny přihlašovací, registrační, odhlašovací a další stránky. Defaultní vzhled (layout.html) také zobrazí odpovídající volby v pravém horním rohu stránek.
A nyní stačí akci předřadit dekorátor, např.:
@auth.requires_login()
def show():
image = db.image(request.args(0,cast=int)) or redirect(URL('index'))
db.comment.image_id.default = image.id
form = crud.create(db.comment, next=URL(args=image.id),
message='váš komentář byl přijat')
comments = db(db.comment.image_id==image.id).select()
return dict(image=image, comments=comments, form=form)
Každý pokus o přístup na adresu:
http://127.0.0.1:8000/images/default/show/[image_id]
bude nyní vyžadovat login. Jestliže uživatel ještě není přihlášen, bude přesměrován na:
http://127.0.0.1:8000/images/default/user/login
user
funkce rovněž zveřejní, kromě dalšího, následující akce:
http://127.0.0.1:8000/images/default/user/logout
http://127.0.0.1:8000/images/default/user/register
http://127.0.0.1:8000/images/default/user/profile
http://127.0.0.1:8000/images/default/user/change_password
http://127.0.0.1:8000/images/default/user/request_reset_password
http://127.0.0.1:8000/images/default/user/retrieve_username
http://127.0.0.1:8000/images/default/user/retrieve_password
http://127.0.0.1:8000/images/default/user/verify_email
http://127.0.0.1:8000/images/default/user/impersonate
http://127.0.0.1:8000/images/default/user/not_authorized
Nyní tedy uživatel, který je na stránkách poprvé, se bude muset registrovat, aby mohl číst a zasílat komentáře.
Obojí,
auth
objekt auser
funkce, jsou už definovány v kostře aplikace (v kopii aplikace Welcome).auth
objekt je vysoce nastavitelný. Může pracovat s emailovou verifikací, schválením registrace administrátory, CAPTCHou, a případně dalšími login methodami za pomoci pluginů.
Přidání gridů (tabulek)
Můžeme vše dále vylepšit za pomoci SQLFORM.grid
nebo SQLFORM.smartgrid
a vytvořit si řídicí interface pro naši aplikaci:
@auth.requires_membership('manager')
def manage():
grid = SQLFORM.smartgrid(db.image)
return dict(grid=grid)
a k této akci odpovídající view "views/default/manage.html"
{{extend 'layout.html'}}
<h2>Řídicí interface</h2>
{{=grid}}
Za pomoci appadmin definujte skupinu "manager" a některého z uživatelů do této skupiny zařaďte. Pak bude moci navigovat na stránku:
http://127.0.0.1:8000/images/default/manage
a procházet seznam a vyhledávat:
vytvářet, editovat a rušit obrázky a komentáře k nim:
Konfigurace vzhledu
Můžete konfigurovat defaultní vzhled editováním "views/layout.html", ale také můžete vzhled ovlivnit bez editování HTML. "static/base.css" stylesheet (popis stylů) je dobře dokumentován a popsán v kapitole 5. Můžete měnit barvy, sloupce, rozměry, okraje a pozadí, aniž byste editovali HTML. Chcete-li editovat menu, titulek nebo podtitulek, můžete to udělat ve kterémkoli modelu (rozuměj: ve kterémkoli souboru v sekci Modely). Vzorová aplikace (získaná kopií Welcome při vytváření další nové aplikace), nastavuje defaultní hodnoty těchto parameterů v souboru "models/menu.py":
response.title = request.application
response.subtitle = T('customize me!')
response.meta.author = 'you'
response.meta.description = 'describe your app'
response.meta.keywords = 'bla bla bla'
response.menu = [ [ 'Index', False, URL('index') ] ]
Wiki
Následující diagram vypisuje akce, které potřebujeme implementovat, a propojení, která musíme v souvislosti s těmito akcemi vytvořit.
Začněte vytvořením nové kostry aplikace (vytvořte novou aplikaci, čímž vznikne kopie Welcome aplikace), kterou pojmenujete "mywiki".
Model bude obsahovat 3 tabulky: page (stránka), comment (komentář) a document. Obojí, comment i document obsahují odkaz na stránku, protože patří ke konkrétní stránce. Document obsahuje pole "file" typu upload, stejné, jaké jsme použili v předchozí aplikaci.
Tady je celý model:
db = DAL('sqlite://storage.sqlite')
from gluon.tools import *
auth = Auth(db)
auth.define_tables()
crud = Crud(db)
db.define_table('page',
Field('title'),
Field('body', 'text'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id),
format='%(title)s')
db.define_table('comment',
Field('page_id', 'reference page'),
Field('body', 'text'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id))
db.define_table('document',
Field('page_id', 'reference page'),
Field('name'),
Field('file', 'upload'),
Field('created_on', 'datetime', default=request.now),
Field('created_by', 'reference auth_user', default=auth.user_id),
format='%(name)s')
db.page.title.requires = IS_NOT_IN_DB(db, 'page.title')
db.page.body.requires = IS_NOT_EMPTY()
db.page.created_by.readable = db.page.created_by.writable = False
db.page.created_on.readable = db.page.created_on.writable = False
db.comment.body.requires = IS_NOT_EMPTY()
db.comment.page_id.readable = db.comment.page_id.writable = False
db.comment.created_by.readable = db.comment.created_by.writable = False
db.comment.created_on.readable = db.comment.created_on.writable = False
db.document.name.requires = IS_NOT_IN_DB(db, 'document.name')
db.document.page_id.readable = db.document.page_id.writable = False
db.document.created_by.readable = db.document.created_by.writable = False
db.document.created_on.readable = db.document.created_on.writable = False
Editujte controller "default.py" a vytvořte následující akce:
- index: vypíše všechny wiki stránky
- create: založí další wiki stránku
- show: zobrazí wiki stránku, komentáře k ní, a umožní přidat komentář
- edit: edituje existující stránku
- documents: spravuje dokumenty, připojené ke stránce
- download: stáhne dokument (podobně jako v příkladu Obrázkový blog)
- search: zobrazí box pro vyhledávání a za pomoci Ajax callbacku bude vracet odpovídající tituly pro začátek textu, který návštěvník píše
- callback: Ajaxová callback funkce. Vrací HTML, které se bude vkládat do zzadaného místa vyhledávací stránky při tom, jak návštěvník pokračuje ve psaní do vyhledávacího boxu.
Tady je "default.py" controller:
def index():
"""zobrazí seznam Wiki stránek
>>> index().has_key('pages')
True
"""
pages = db().select(db.page.id,db.page.title,orderby=db.page.title)
return dict(pages=pages)
@auth.requires_login()
def create():
"založí novou prázdnou Wiki stránku"
form = crud.create(db.page, next=URL('index'))
return dict(form=form)
def show():
"zobrazí Wiki stránku"
this_page = db.page(request.args(0,cast=int)) or redirect(URL('index'))
db.comment.page_id.default = this_page.id
form = crud.create(db.comment) if auth.user else None
pagecomments = db(db.comment.page_id==this_page.id).select()
return dict(page=this_page, comments=pagecomments, form=form)
@auth.requires_login()
def edit():
"Edituje existující Wiki stránku"
this_page = db.page(request.args(0,cast=int)) or redirect(URL('index'))
form = crud.update(db.page, this_page,
next=URL('show',args=request.args))
return dict(form=form)
@auth.requires_login()
def documents():
"prohlížení a editace všech dokumentů, propojených se stránkou"
page = db.page(request.args(0,cast=int)) or redirect(URL('index'))
db.document.page_id.default = page.id
db.document.page_id.writable = False
grid = SQLFORM.grid(db.document.page_id==page.id,args=[page.id])
return dict(page=page, grid=grid)
def user():
return dict(form=auth())
def download():
"umožní stahování dokumentů"
return response.download(request, db)
def search():
"ajax vyhledávací stránka"
return dict(form=FORM(INPUT(_id='keyword',_name='keyword',
_onkeyup="ajax('callback', ['keyword'], 'target');")),
target_div=DIV(_id='target'))
def callback():
"ajaxový callback, který vrací <ul> s odkazy na Wiki stránky"
query = db.page.title.contains(request.vars.keyword)
pages = db(query).select(orderby=db.page.title)
links = [A(p.title, _href=URL('show',args=p.id)) for p in pages]
return UL(*links)
Řádky 2-5 jsou komentářem (popisem) akce "index". Řádky 3-4 uvnitř komentáře jsou Pythonem interpretovány jako test kód (doctest). Testy můžete spouštět z admin rozhraní. V tomto příkladu test verifikuje, zda "index" akce proběhne bez chyb.
Řádky 18, 27 a 35 se pokouší získat záznam s tabulky page
s id podle request.args(0)
.
Řádky 13, 20 definují a řídí zadávací formuláře pro novou stránku a pro nový komentář.
Řádek 28 definuje a řídí editační formulář pro úpravy existující Wiki stránky.
Řádek 38 vytvoří grid
objekt, který umožňuje procházet, přidávat a editovat komentáře, propojené se stránkou.
Něco obtížnějšího se děje na řádku 51. Je tu nastaven onkeyup
atribut INPUT tagu "keyword". Vždy, když návštěvník uvolní klávesu, provede se JavaScript kód onkeyup
atributu, samozřejmě na straně klienta (v browseru). Jedná se o tento JavaScript kód:
ajax('callback', ['keyword'], 'target');
ajax
je javascriptová funkce, definovaná v souboru "web2py.js", který je připojen pomocí defaultního "layout.html". Potřebuje 3 parametery: URL akce, která vykonává synchronní callback, seznam ID proměnných, které chceme poslat do callbacku (["keyword"]), a ID, kam bude vložena odpověď (v našem případě "target").
Jakmile cokoli píšete do vyhledávacího boxu, tak po uvolnění klávesy klient zavlá server a pošle obsah 'keyword' pole, a, jakmile server odpoví, odpověď bude vložena do stránky, jako innerHTML 'target' tagu.
'target' tag je DIV, definovaný na řádku 52. Nebyl by také problém definovat ho až ve view.
Tady je kód view "default/create.html":
{{extend 'layout.html'}}
<h1>Vytvoř novou Wiki stránku</h1>
{{=form}}
Navštívíte-li create stránku, uvidíte následující:
Tady je kód view "default/index.html":
{{extend 'layout.html'}}
<h1>Dostupné Wiki stránky</h1>
[ {{=A('search', _href=URL('search'))}} ]<br />
<ul>{{for page in pages:}}
{{=LI(A(page.title, _href=URL('show', args=page.id)))}}
{{pass}}</ul>
[ {{=A('vytvoř stránku', _href=URL('create'))}} ]
což generuje následující stránku:
Tady je kód view "default/show.html":
{{extend 'layout.html'}}
<h1>{{=page.title}}</h1>
[ {{=A('edituj', _href=URL('edit', args=request.args))}}
| {{=A('documenty', _href=URL('documents', args=request.args))}} ]<br />
{{=MARKMIN(page.body)}}
<h2>Komentáře</h2>
{{for comment in comments:}}
<p>{{=db.auth_user[comment.created_by].first_name}} v {{=comment.created_on}}
napsal(a) <i>{{=comment.body}}</i></p>
{{pass}}
<h2>Přidej komentář</h2>
{{=form}}
Pokud byste chtěli používat Markdown syntaxi místo syntaxe Markmin:
from gluon.contrib.markdown import WIKI
a použijte WIKI
místo helperu MARKMIN
. A nebo můžete použít přímo HTML místo symbolické syntaxe (místo Markmin syntaxe). V takovém případě nahraďte:
{{=MARKMIN(page.body)}}
takto:
{{=XML(page.body)}}
XML nebude escapováno, jako je tomu při běžném chování Web2py.
To můžete lépe udělat takto:
{{=XML(page.body, sanitize=True)}}
Nastavením sanitize=True
řeknete Web2py, aby escapovalo nebezpečné XML tagy, jako třeba "<script>" a tím aby bránilo proti XSS vulnerabilities.
Nyní, jestliže z "index" stránky klepnete na titulek stránky, můžete vidět stránku, kterou jste vytvořili:
Tady je kód view "default/edit.html":
{{extend 'layout.html'}}
<h1>Edituj Wiki stránku</h1>
[ {{=A('zobraz', _href=URL('show', args=request.args))}} ]<br />
{{=form}}
To vytvoří stránku, která vypadá téměř identicky s "create" stránkou.
Zde je kód view "default/documents.html":
{{extend 'layout.html'}}
<h1>Dokumenty ke stránce: {{=page.title}}</h1>
[ {{=A('zobraz', _href=URL('show', args=request.args))}} ]<br />
<h2>Documenty</h2>
{{=grid}}
Jestliže z "show" stránky klepnete na Dokumenty, můžete nyní spravovat dokumenty, propojené se stránkou.
A nakonec tady je kód view "default/search.html":
{{extend 'layout.html'}}
<h1>Hledej ve Wiki stránkách</h1>
[ {{=A('vypiš vše', _href=URL('index'))}}]<br />
{{=form}}<br />{{=target_div}}
který vygeneruje následující ajaxový vyhledávací formulář:
Můžete také zkusit zavolat callback akci přímo, tak, že např. navštívíte následující URL:
http://127.0.0.1:8000/mywiki/default/callback?keyword=wiki
Podíváte-li se na zdrojový kód stránky, uvidíte HTML, které callback vrátil:
<ul><li><a href="/mywiki/default/show/4">I made a Wiki</a></li></ul>
Generování RSS kanálu se změnami stránek je při použití Web2py jednoduché, protože součástí Web2py je knihovna gluon.contrib.rss2
. Jednoduše přidejte novou akci do default controlleru:
def news():
"generuje rss kanál z Wiki stránek"
reponse.generic_patterns = ['.rss']
pages = db().select(db.page.ALL, orderby=db.page.title)
return dict(
title = 'myWiki rss kanál',
link = 'http://127.0.0.1:8000/mywiki/default/index',
description = 'myWiki novinky',
created_on = request.now,
items = [
dict(title = row.title,
link = URL('show', args=row.id),
description = MARKMIN(row.body).xml(),
created_on = row.created_on
) for row in pages])
a když přejdete na:
http://127.0.0.1:8000/mywiki/default/news.rss
uvidíte rss kanál (přesný výstup závisí na použité rss čtečce). Poznamenejme, že dict (dictionary) je automaticky konvertována na RSS díky tomu, že jsme použili příponu .rss v adrese.
Web2py také obsahuje rss parser pro zpracování/čtení rss kanálů třetích stran.
Nakonec si přidejme XML-RPC handler, který umožní prohledávat Wiki pomocí externích programů:
service = Service()
@service.xmlrpc
def find_by(keyword):
"najde stránky, které obsahují zadané klíčové slovo; pro XML-RPC"
return db(db.page.title.contains(keyword)).select().as_list()
def call():
"zveřejní všechny registrované webové služby, včetně XML-RPC"
return service()
Zde akce ovladače (handler action; "call") zveřejní funkce, označené dekorátorem @service...., v našem případě funkci find_by
. find_by
není akce, ale jen funkce (protože požaduje argument). Dotáže se datbáze pomocí .select()
a pak převede vrácené záznamy na seznam (list).
Tady máme příklad, jak XML-RPC handler používat z externího programu (v tomto příkladu je opět napsán v Pythonu):
>>> import xmlrpclib
>>> server = xmlrpclib.ServerProxy(
'http://127.0.0.1:8000/mywiki/default/call/xmlrpc')
>>> for item in server.find_by('wiki'):
print item['created_on'], item['title']
Handler ale může být používán ze kteréhokoli programovacího jazyka, který umí s XML-RPC pracovat, např. z C, C++, C# nebo Javy.
O date
, datetime
a time
formátech
Jsou 3 různé reprezentace každého z datových typů: date
, datetime
a time
:
- uložení v databázi
- interní Web2py reprezentace
- řetězcová reprezentace ve formulářích a HTML tabulkách
Databázová reprezentace je vnitřní záležitost a nemá vliv na kód. Interně, na úrovni Web2py, jsou tyto typy uloženy jako datetime.date
, datetime.datetime
nebo datetime.time
objekty, a jako s takovými s nimi můžeme pracovat, např.:
for page in db(db.page).select():
print page.title, page.day, page.month, page.year
Při konverzi datových typů na řetězce ve formulářích jsou datové typy převáděny s použitím ISO reprezentace
%Y-%m-%d %H:%M:%S
Tato reprezentace ale podléhá internacionalizaci (automatickému jazykovému překladu), takže můžete v administračním rozhraní použít překladovou (translation) stránku a změnit formát na jakýkoli jiný. Např.:
%m/%b/%Y %H:%M:%S
Pamatujte, že angličtina (English) se defaultně nepřekládá, protože Web2py se domnívá, že aplikace je napsána v angličtině. Chcete-li, aby internacionalizace pracovala i pro angličtinu, musíte vytvořit překladový soubor (za pomoci admin rozhraní) a musíte zadat, že aktuálním jazykem aplikace je něco jiného než angličtina, např.:
T.current_languages = ['null']
Více o admin
Administrační rozhraní poskytuje přídavnou funkcionalitu, kterou si nyní stručně popíšeme zde.
site
Tato stránka vypisuje všechny nainstallované aplikace. Jsou tu také dva formuláře.
První z nich umožňuje vytvořit novou aplikaci zadáním jejího jména. Už jsme řekli, že nová aplikace se založí jako kopie aplikace Welcome. Obsahuje základní kostru aplikace, a pokud tedy chceme psát aplikaci zcela od základu, musíme smazat obsah základních používaných souborů (modelu, controlleru, ..).
Druhý formulář umožňuje uploadovat (instalovat) existující aplikaci buď ze souboru nebo ze vzdálené URL. Když uploadujete aplikaci, je také potřeba zadat její jméno. Může to být buď původní jméno nebo jiné. To umožňuje nainstalovat i více kopií od jedné aplikace. Vyzkoušejte sii např. uploadovat Instant Press CMS (redakční systém) od Martina Mulone z adresy:
http://code.google.com/p/instant-press/
Web2py sobory jsou zabaleny s příponou
.w2p
. Jedná se o tar-gzip archivy. Web2py používá příponu.w2p
místo.tgz
, aby zabránilo browseru v rozbalení archivu během downloadu. Můžete je samozřejmě rozbalit manuálně (např. v Linuxu pomocítar zxvf [filename]
), ale pro proces instalace to není potřeba.
Po úspěšném uploadu Web2py zobrazí MD5 kontrolní součet uploadovaného souboru. Můžete ho využít ke kontrole, že nedošlo k poškození souboru při přenosu. Po uploadu se jméno InstantPress objeví v seznamu instalovaných aplikací.
Klepněte na jméno InstantPress v admin rozhraní a aplikace se spustí.
O Instant Press CMS si můžete přečíst více na adrese:
http://code.google.com/p/instant-press/
Pro každou instalovanou aplikaci vám site stránka (hlavní stránka admin rozhraní) umožňuje:
- Uninstall - odinstalovat aplikaci.
- Přejít na about stránku (viz níže).
- Přejít na edit stránku (viz níže).
- Přejít na errors stránku (viz níže).
- Clean up - odstranit dočasné soubory: sessiony, chybové tikety a cache.disk soubory (diskovou cache).
- Pack all - zabalit aplikaci. Tím získáte tar archiv s úplnou kopií aplikace. Předtím je vhodné odstranit dočasné soubory (clean up).
- Compile - kompilovat, přeložit aplikaci. Nejsou-lli nalezeny žádné chyby, tato volba zkompiluje všechny modely, controllery a view do bytecode. Protože view mohou připojovat jiná view pomocí extend (vřadí aktuální view dovnitř rámcového) a include (vřadí jiné view dovnitř aktuálního), je před bytecode kompilací nejprve celý view strom pro každý controller transformován do jediného souboru. Efektem bytecode kompilace je, že aplikace je rychlejší, protože se vyloučí parsování šablon a náhrady řetězců během runtime.
- Pack compiled. Tato volba je k dispozici jen pro bytecode kompilované aplikace. Umožní zabalit aplikaci bez zdrojového kódu pro distribuci jako closed source (např. komerční aplikace). Poznamenejme, že Python (jako každý jiný programovací jazyk) může být dekompilován; proto kompilace neznamená úplnou ochranu zdrojových kódů. Dekompilace nicméně pravděpodobně bude obtížná a také může být ilegální.
- Remove compiled - odstraň kompilované. To odstraní byte-code kompilované modely, view a controllery z aplikace. Byla-li aplikace zabalena se zdrojovým kódem nebo lokálně editovaná, odstranění bytecode kompilovaných souborů nezpůsoobí problém a aplikace bude nadále plně provozuschopná. Ale pro aplikaci instalovanou ze zabaleného kompilovaného souboru toto znamená, že přestane definitivně pracovat, protože nemáme zdrojový kód, aby bylo možné ji znovu zprovoznit.
Veškerá funkcionalita Web2py administrační site stránky (hlavní admin stránky) je také dostupná programově pomocí API rozhraní, definovaného v modulu
gluon/admin.py
. Jednoduše importujte tento modul v Python kódu nebo z Python shellu.
about
about záložka umožňuje editovat popis aplikace a její licenci. Ty jsou uloženy v ABOUT a LICENSE souborech ve složce aplikace.
Můžete použít MARKMIN
nebo gluon.contrib.markdown.WIKI
syntaxi pro tyto soubory, jak je popsáno v odkazu.[markdown2] .
edit
edit stránku jste již používali při práci s touto kapitolou. Tady se podíváme na další funkcionalitu edit stránky.
- Kliknete-li na kterékoli jméno souboru, uvidíte obsah souboru se zvýrazněním syntaxe.
- Kliknete-li na "edit", you can edit the file via a web interface.
- Kliknete-li na "delete", bude smazán příslušný soubor (definitivně).
- Kliknete-li na "test", Web2py provede testy. Testy jsou psány vývojářem jako standardní Python doctesty a je vhodné, aby každá funkce měla vlastní test.
- Můžete přidat jazykový (překladový) soubor, projít aplikaci, aby se aktivovaly všechny jazykově závislé řetězce, a editovat překlady řetězců pomocí webového rozhraní.
- Jsou-li statické soubory rozčleněny do složek a podsložek, hierarchie složek může být rozbalována nebo sbalována poklepáním na jméno složky (adresáře).
Obrázek ukazuje výstup testovací stránky Welcome aplikace.
Obrázek níže ukazuje jazykovou záložku Welcome aplikace.
Obrázek níže ukazuje, jak editovat jazykový soubor, v tomto případě "it" (italštinu) pro Welcome aplikaci.
shell
Klepnete-li na odkaz "shell" v záložce controllers, Web2py otevře webové rozhraní Python shellu, když předtím provedlo všechny modely aktuální aplikace. To umožňuje interaktivně komunikovat s aplikací, ladit a zkoušet.
crontab
Rovněž v záložce controllers je odkaz "crontab". Klepnutím na tento odkaz můžete editovat Web2py crontab soubor (plánování automatických úloh). Syntaxe je jako pro unixový crontab, ale bez závislosti na Unixu. Je potřeba pouze Web2py a plánování úloh funguje i ve Windows. Zaregistrujte zde akce, které potřebujete vykonat na pozadí, v plánovaných časech. Více se o tom dočtete v další kapitole.
errors
Když programujete, nevyhnutelně děláte chyby a zanášíte vady (bugs) do softwaru. Web2py pomáhá dvěma způsoby: 1) umožňuje vám vytvořit testy pro každou funkci; tyto testy lze spustit v prohlížeči, z edit stránky; a 2) když dojde k chybě, je vystaven tiket o chybě pro návštěvníka a informace o chybě jsou zapsány do logu.
Například záměrně přidejte chybný příkaz do "images" aplikace takto:
def index():
images = db().select(db.image.ALL,orderby=db.image.title)
1/0
return dict(images=images)
Když zavoláte "index" akci, dostanete následující tiket:
Jen administrátor může tiket otevřít a zjišťovat informace o chybě:
Ticket ukazuje traceback (hierarchii volání), obsah souboru, který způsobil problém, a kompletní stav systému (proměnné, request, session, apod.) Jestliže k chybě dojde ve view, Web2py zobrazí view konvertované z HTML podoby do Python kódu. To umožňuje snáze pochopit logickou strukturu souboru.
Defaultně se tikety ukládají do filesystému (tj. jako soubory) a seskupují podle tracebacku (sdruží se tikety se shodnou hierarchií volání). Administrační rozhraní tedy nabízí agregovaný pohled (je zobrazen typ tracebacku a celkový počet výskytů chyby) a detailní pohled (vypsány jsou všechny tikety podle jejich ticket id). Administrátor může volit a přepínat mezi těmito dvěma pohledy.
Poznamenejme, že kdekoli admin zobrazuje kód, zvýrazňuje syntaxi (např. v chybových výpisech jsou Web2py kklíčová slova zobrazena jako oranžová). Klepnete-li na Web2py klíčové slovo, budete přesměrováni na stránku dokumentace o tomto klíčovém slově.
Jestliže opravíte divide-by-zero bug (dělení nulou) v akci "index" a nyní zanesete stejnou chybu do view "index":
{{extend 'layout.html'}}
<h1>Přehled obrázků</h1>
<ul>
{{for image in images:}}
{{1/0}}
{{=LI(A(image.title, _href=URL("show", args=image.id)))}}
{{pass}}
</ul>
dostanete takovýto tiket:
Web2py konvertovalo view z HTML do Python souboru a vystavený chybový tiket odkazuje na generovaný Python kód, místo na původní view soubor:
Napoprvé to může mást. Ale v praxi zjistíte, že je to pro ladění přínosem, protože pythonovské odsazování (indentation) vám zvýrazní logickou strukturu kódu, který jste zapsali do view.
Kód je vypsán na stejné stránce níže.
Všechny chybové tikety jsou vypsány pod adminem na errors stránce každé aplikace:
Mercurial
Používáte-li aplikaci se zdrojovými soubory a máte instalovány Mercurial version control libraries (knihovny pro práci s Mercurial):
easy_install mercurial
pak admin rozhraní ukazuje další menu navíc, nazvané "mercurial". Automaticky se vytvoří lokální Mercurial repozitář pro aplikaci. Stisknutím tlačítka "commit" commitujete aktuální stav aplikace, tj. zapíšete tento commit jako dílčí verzi aplikace do Mercurial repozitáře. Mercurial vytvoří a uloží informace o změnách, které jste v kódu od posledně provedl(a) a zapíše je do svého skrytého adresáře ".hg" ve složce aplikace. Každá aplikace má svůj ".hg" adresář a svůj ".hgignore" soubor (který Mercurialu říká, které soubory ve složce a podsložkách aplikace má zcela ignorovat - neboli nemá sledovat).
Mercurial webové rozhraní umožňuje zobrazit předcházející commit a rozdílové (diff) soubory, ale doporučujeme, abyste Mercurial používali přímo ze shellu nebo z některého grafického (GUI-based) Mercurial klienta, protože jsou samozřejmě mnohem výkonnější. Zejména vám umožní synchronizovat vaši aplikaci se vzdáleným repozitářem:
O Mercurial se dočtete víc tady:
http://mercurial.selenic.com/
a samozřejmě je možné používat místo Mercurialu jemu podobný Git.
Admin wizard (experimentální)
admin rozhraní obsahuje Wizard, který vám může pomoci vytvořit nové aplikace. Wizard spustíte ze "site" stránky (hlavní stránky admina), jak ukazuje obrázek:
Wizard vás provede sérií kroků, potřebných pro vytvoření nové aplikace:
- Vyberte jméno pro novou aplikaci
- Konfigurujte aplikaci a vyberte požadované pluginy
- Sestavte požadované modely; budou vytvořeny CRUD stránky pro každou tabulku
- Máte možnost editovat view těchto stránek za pomoci MARKMIN syntaxe
Obrázek níže ukazuje druhý krok tohoto postupu.
Je zde dropdown prvek pro výběr vzhledového pluginu (from web2py.com/layouts
), dropdown prvek s možností vícenásobného výběru pro výběr funkčních pluginů (from web2py.com/plugins
) a "login config" pole, kam můžete zapsat Janrain údaje: "domain:key".
Další kroky jsou dostatečně jasné a není třeba je popisovat.
Wizard plní dobře svůj účel, ale přesto jsme ho označili jako experimentální vlastnost, ze dvou důvodů:
- Aplikace wizardem vytvořená a později editovaná manuálně nemůže už být dodatečně upravována wizardem.
- Rozhraní wizardu se pravděpodobně bude měnit, aby byla přidána podpora dalších vlastností a zjednodušen vývoj.
V každém případě je wizard zajímavým nástrojem pro rychlé vytváření "nástřelu" aplikace.
Konfigurování admin aplikace
Normálně není potřeba admin aplikaci konfigurovat, ale některé konfigurace jsou možné. Po přihlášení do admina můžete měnit konfigurační soubor na adrese:
http://127.0.0.1:8000/admin/default/edit/admin/models/0.py
Vidíme, že admin může být používán, aby editoval sám sebe. admin je ve skutečnosti aplikace jako kterákoli jiná.
Soubor "0.py" je velmi dobře dokumentován sám sebou a jestliže jej otevřete, zřejmě již budete vědět, co chcete změnit. Některá přizpůsobení jsou nicméně důležitější než ostatní:
GAE_APPCFG = os.path.abspath(os.path.join('/usr/local/bin/appcfg.py'))
Tato hodnota má směřovat na umístění "appcfg.py" souboru, který je součástí Google App Engine SDK. Jestliže GAE SDK používáte, je potřeba se ujistit, že tento parametr ukazuje do správného umístění. To vám umožní nasazovat aplikace do produkčního prostředí (deploy) na GAE z admin rozhraní.
Můžete také přepnout Web2py admina do demo módu:
DEMO_MODE = True
FILTER_APPS = ['welcome']
Pak jen aplikace, vypsané ve "filter apps" budou dostupné, a budou přístupné jen v read-only módu.
Jste-li učitel a chcete zpřístupnit administrační rozhraní studentům tak, aby studenti používali jediné admin rozhraní pro své projekty (něco jako virtuální laboratoř - virtual lab), nastavte:
MULTI_USER_MODE = True
V tomto případě se studenti musí zalogovat a budou mít přístup pouze ke svým aplikacím pomocí admin. Vy, jakožto první uživatel/učitel, máte přístup ke všem.
Pamatujte ale, že tento mechanismus počítá i tak s tím, že všem studentům důvěřujete. Všechny aplikace, vytvořené pod admin běží se stejnými údaji na stejném souborovém systému. Studentem vytvořená aplikace má tedy přístup i k datům a zdrojovým kódům aplikací, kteeré vytvořili ostatní studenti.
Více o appadmin
appadmin není zamýšlen jako finální rozhraní, které dáte k dispozici jako součást aplikace. Jeho účelem je poskytnout vývojáři snadný přístup do databáze. Sestává jen ze dvou souborů: controlleru "appadmin.py" a view "appadmin.html", které používají všechny akce v controlleru.
appadmin controller je poměrně malý a čitelný soubor; je to určitý příklad, jak je možné designovat databázové rozhraní.
appadmin ukazuje, se kterými databázemi pracujeme, a jaké jsou v nich tabulky. V jednotlivých tabulkách můžete vkládat záznamy a vypisovat záznamy. appadmin stránkuje výstup po 100 záznamech.
Vybrané záznamy můžete omezit podmínkou (Query). Máte možnost je také hromadně aktualizovat (update) nebo rušit (delete). V obou případech zkontrolujte Query podmínku, abyste neovlivnili více záznamů, než chcete.
Pro update záznamů zašrtněte checkbox u Update a zadejte požadované přiřazení:
title = 'test'
kde řetězcové hodnoty je nutno uzavírat doo jednoduchých uvozovek. Více polí lze případně oddělovat čárkami.
Pro zrušení záznamů klepněte na odpovídající checkbox.
Stisknutím Submit (Potvrdit) se změny provedou.
appadmin také dokáže vytvářet joiny, jestliže Query bude obsahovat SQL podmínku, která zahrnuje více než jednu tabulku. Např. vyzkoušejte:
db.image.id == db.comment.image_id
Web2py to předá DAL (Databázové Abstrakční Vrstvě) a ta rozumí, že dotaz spojuje dvě tabulky; spojí tedy obě tabulky pomocí INNER JOIN. Výsledek je zde:
Kliknete-li na číslo v poli id (v seznamu vybraných záznamů), dostanete editační stránku pro záznam s právě zvoleným id. V ní je také možné (zcela dole) záznam zrušit.
Kliknete-li na cizí klíč (odkazující pole; reference field), zobrazí se editační stránka pro odkazovaný záznam.
Nelze aktualizovat nebo rušit v řádcích, získaných joinem. Jsou totiž získány z více tabulek a ovládání ve vztahu k tomu, co chceme skutečně provést, by bylo matoucí.
Kromě databázové administrace appadmin také umožňuje si prohlížet detaily obsahu aplikační cache
(na adrese /yourapp/appadmin/ccache
) a obsah aktuálních request
, response
a session
objektů (na /yourapp/appadmin/state
).
appadmin nahrazuje
response.menu
svým vlastním menu, které nabízí např. odkaz na edit stránku aplikace, db stránku (hlavní stránku appadmin-a), state stránku (request, response a session objekty) a cache stránku (správa cachování). V případě, že váš aplikační vzhled negeneruje menu za pomociresponse.menu
, mohlo by se stát, že appadmin menu nebude viditelné. V takovém případě můžete pro nápravu upravit appadmin.html soubor a přidat:{{=MENU(response.menu)}}