Chapter 4: The core

Jádro

Parametry příkazové řádky

Je možné přeskočit spuštění GUI (grafického uživatelského prostředí) a spustit web2py přímo z příkazové řádky např. zápisem:

password
python web2py.py -a 'vaše heslo' -i 127.0.0.1 -p 8000

Uvedete-li místo 'vaše heslo' parametr '<ask>', Web2py se na heslo zeptá interaktivně. Web2py při spuštění vytvoří soubor "parameters_8000.py", kam uloží hashované heslo.

Bezpečnost zvýšíte, když spustíte Web2py takto:

python web2py.py -a '<recycle>' -i 127.0.0.1 -p 8000

Web2py v tomto případě použije předchozí uložené heslo. Nebylo-li heslo zadáno nebo jestliže jste soubor "parameters_8000.py" smazali, přístup do webového administračního rozhraní není dovolen.

PAM

Na některých Unix/Linux systémech, jestliže heslo je:

<pam_user:some_user>

použije Web2py PAM heslo účtu operačního systému uživatele some_user k autentifikaci administrátora, pokud to ovšem není zakázáno PAM konfigurací.

Web2py normálně běží pomocí CPython-u (C implementace Python interpreteru, kterou původně vytvořil Guido van Rossum), ale může také běžet za pomoci PyPy (implementace interpreteru přímo v Pythonu) nebo Jython-u (Java implementace interpreteru). Uvedená poslední varianta umožňuje používání Web2py v kontextu J2EE infrastruktury. Jednoduše zaměňte příkaz "python", např. když chcete použít Jython, nahraďte "python web2py.py ..." příkazem "jython web2py.py". Detaily o instalaci Jython-u a zxJDBC modulů, potřebných pro přístup k databázi, najdete v kapitole 14.

"web2py.py" skript rozumí mnoha parametrúm příkazové řádky, např. určujícím množství vláken (threads), povolení šifrovaného spojení (SSL), apod. Pro úplný výčet parametrů napište:

command line
>>> python web2py.py -h
Usage /Použití/: python web2py.py

web2py Web Framework startup script /spouštěcí skript Web2py frameworku/.
ATTENTION: unless a password is specified (-a 'passwd'), web2py will attempt to run a GUI. In this case command line options are ignored. /POZOR: neuvedete-li heslo (-a parametr) Web2py spustí GUI a v tom případě bude ignorovat parametry příkazové řádky./

Options /Možnosti/:
  --version             ukaž číslo verze a skonči
  -h, --help            ukaž tuto nápovědu a skonči
  -i IP, --ip=IP        ip adresa serveru (default je 127.0.0.1)
  -p PORT, --port=PORT  port serveru (8000)
  -a PASSWORD, --password=PASSWORD
                        heslo pro administrační přístup (použijte -a
                        "<recycle>" pro použití minulého hesla)
  -c SSL_CERTIFICATE, --ssl_certificate=SSL_CERTIFICATE
                        soubor se SSL certifikátem
  -k SSL_PRIVATE_KEY, --ssl_private_key=SSL_PRIVATE_KEY
                        soubor se SSL privátním klíčem
  --ca-cert=SSL_CA_CERTIFICATE
                        použijte tento soubor, obsahující CA certifikát, k validaci X509 certifikátů od klientů
  -d PID_FILENAME, --pid_filename=PID_FILENAME
                        soubor pro uložení pid (proces id) serveru
  -l LOG_FILENAME, --log_filename=LOG_FILENAME
                        soubor pro logování připojení
  -n NUMTHREADS, --numthreads=NUMTHREADS
                        počet vláken (zastaralé /deprecated/)
  --minthreads=MINTHREADS
                        minimální počet serverových vláken
  --maxthreads=MAXTHREADS
                        maximální počet serverových vláken
  -s SERVER_NAME, --server_name=SERVER_NAME
                        jméno pro webový server
  -q REQUEST_QUEUE_SIZE, --request_queue_size=REQUEST_QUEUE_SIZE
                        maximální počet požadavků ve frontě při nedostupnosti serveru
  -o TIMEOUT, --timeout=TIMEOUT
                        timeout pro individuální požadavek (10 sekund)
  -z SHUTDOWN_TIMEOUT, --shutdown_timeout=SHUTDOWN_TIMEOUT
                        timeout pro vypnutí serveru /on shutdown of server/
                        (5 sekund)
  --socket-timeout=SOCKET_TIMEOUT
                        timeout pro socket (5 second)
  -f FOLDER, --folder=FOLDER
                        adresář, z něhož se má Web2py spouštět
  -v, --verbose         zvýší počet hlášení při parametru --test
  -Q, --quiet           zakáže veškerý výstup (výpisy na konzoli)
  -D DEBUGLEVEL, --debug=DEBUGLEVEL
                        nastaví úroveň výstupů ladění
                        (0-100, 0 znamená vše, 100 znamená nic; default je 30)
  -S APPNAME, --shell=APPNAME
                        spustí web2py v interaktivním shellu nebo IPython-u
                        (je-li instalován) pro uvedenou aplikaci (jestliže
                        aplikace neexistuje, bude vytvořena v úvodní podobě).
                        APPNAME zadat jako a/c/f (c,f volitelně)
                          /aplikace/kontrolér/funkce/
  -B, --bpython         spustí web2py v interaktivním shellu nebo bpython-u
                        (je-li instalován) pro uvedenou aplikaci (jestliže
                        aplikace neexistuje, bude vytvořena v úvodní podobě).
                        Použijte v kombinaci se --shell parametrem.
  -P, --plain           použij obyčejný python shell;
                        použijte v kombinaci se --shell parametrem.
  -M, --import_models   do shellu importuj model zadané aplikace;
                        použijte v kombinaci se --shell parametrem.
  -R PYTHON_FILE, --run=PYTHON_FILE
                        spusť PYTHON_FILE ve web2py prostředí;
                        použijte v kombinaci se --shell parametrem.
  -K SCHEDULER, --scheduler=SCHEDULER
                        spusť plánované úlohy pro uvedené aplikace;
                        uveďte seznam jmen aplikací -K app1,app2,app3
                        nebo seznam app:groups, např.
                          -K app1:group1:group2,app2:group1
                        /..to override specific group_names../.
                        (povoleny jsou jen řetězce, nikoli mezery).
                        Vyžaduje scheduler, definovaný v modelu.
  -X, --with-scheduler  spusť schedulery spolu s webovým serverem
  -T TEST_PATH, --test=TEST_PATH
                        spusť doctesty ve web2py prostředí; TEST_PATH jako
                        a/c/f (c,f volitelně)
  -W WINSERVICE, --winservice=WINSERVICE
                        instaluj|spusť|zastav jako Windows službu
  -C, --cron            spusť cron manuálně;
                        obvykle vyvoláno ze system crontab
  --softcron            vyvolá použití softcron-u
  -N, --no-cron         nespouštěj cron automaticky
  -J, --cronjob         identifikace úlohy cronu
  -L CONFIG, --config=CONFIG
                        configurační soubor
  -F PROFILER_FILENAME, --profiler=PROFILER_FILENAME
                        profiler soubor
  -t, --taskbar         použij web2py GUI (grafické uživatelské rozhraní)
                        a pak běž v taskbaru (v system tray)
  --nogui               jen textově, bez GUI
  -A ARGS, --args=ARGS  seznam argumentů, které budou předány skriptu;
                        pro použití s -S (nebo spíše -R ?),
                            -A musí být uvedeno jako poslední 
  --no-banner           bez informační hlavičky
  --interfaces=INTERFACES
                        naslouchej na více adresách:
                  "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..."
                        (:cert:key volitelné; bez mezer)
  --run_system_tests    spusť web2py testy

Malá písmena jako parametry jsou použita ke konfiguraci webového serveru. -L volba říká Web2py, aby konfiguracci načetlo ze souboru, -W instaluje Web2py jako Windows službu. -S, -P a -M volby spouštějí interaktivní Python shell (při -M je načten model). -T volba spustí doctesty ve Web2py prostředí. Např. následující příklad spustí doctesty ze všech kontrolérů "welcome" aplikace:

python web2py.py -vT welcome

Spouštíte-li Web2py jako Windows službu, -W, není praktické zadávat konfiguraci pomoci parametrů příkazové řádky. Z toho důvodu je ve Web2py složce vzorový "options_std.py" konfigurační soubor interního web serveru:

import socket
import os

ip = '0.0.0.0'
port = 80
interfaces=[('0.0.0.0',80)]
#interfaces.append(('0.0.0.0',443,'ssl_private_key.pem','ssl_certificate.pem'))
password = '<recycle>'  # ## <recycle> means use the previous password
pid_filename = 'httpserver.pid'
log_filename = 'httpserver.log'
profiler_filename = None
minthreads = None
maxthreads = None
server_name = socket.gethostname()
request_queue_size = 5
timeout = 30
shutdown_timeout = 5
folder = os.getcwd()
extcron = None
nocron = None

Tento soubor obsahuje defaultní nastavení Web2py. Jestliže tato nastavení změníte, je potřeba konfiguraci explicitně importovat pomocí -L parametru příkazové řádky. To funguje jen v případě, že Web2py spouštíte jako Windows službu.

Pracovní postup

Pracovní postup /workflow/ Web2py je následující:

  • HTTP požadavky /requests/ přicházejí na web server (vestavěný Rocket server nebo na jiný server, je-li k Web2py připojen pomocí WSGI nebo jiného adaptéru). Web server zpracovává každý požadavek v jeho vlastním vlákně (paralelně).
  • HTTP hlavička požadavku je rozebrána (parsována) a předána do dispatcher-u (vysvětlen v této kapitole níže).
  • Dispatcher určí, která z instalovaných aplikací obslouží požadavek a mapuje cestu (PATH_INFO) v URL na volání funkce. Každému URL je přiřazeno nějaké volání funkce.
  • Požadavky na soubory ve složce 'static' jsou vyřízeny přímo, s tím, že velké soubory jsou pro klienta automaticky streamovány.
  • Požadavky na cokoli jiného než soubory ze složky 'static' jsou mapovány na akce, což jsou funkce v kontroléru (tj. v souboru v adresáři 'controllers').
  • Před voláním akce dojde k několika věcem: jestliže hlavička požadavku obsahuje session cookie konkrétní aplikace, session objekt je načten; jestliže ne, je vytvořeno session id (ale session soubor bude uložen až později); je vytvořeno prováděcí prostředí /execution environment/ pro požadavek; modely se vykonají v tomto prostředí.
  • Pak se v připraveném prostředí provede akce z kontroléru.
  • Vrátí-li akce řetězec je tento řetězec přímo předán klientovi (prohlížeči). Vrátí-li akce HTML helper, je serializován na řetězec a také předán přímo klientovi.
  • Vrátí-li akce iterovatelná data, jsou odeslána kleintovi ve smyčce.
  • Obvykle ovšem akce vrací slovník /dictionary/. Web2py se v takovém případě pokusí najít view (pohled, template) k renderování (formátovanému výpisu) hodnot ze slovníku. View musí být ve shodném adresáři se jménem kontroléru, musí mít shodné jméno se jménem akce (nebyl-li v akci příkaz pro použití jiného view) a tutéž příponu (extension) jako požadovaná stránka (defaults je .html); pokud takové view neexistuje, Web2py použije generické (defaultní) view (když existuje a když je povoleno; jinak ohlásí chybu - neexistující view). View vidí všechny proměnné, definované v modelech, a proměnné ze slovníku, který vrátila akce kontroléru.
  • Celý uživatelský kód je proveden v jediné transakci (pokud není zadáno jinak).
  • Když je uživatelský kód bez chyby, transakce je potvrzena (commitována).
  • Když je v uživatelském kódu (případně v kódu frameworku) neošetřená chyba, transakce je zamítnuta (revertována). Hierarchie volání (traceback) je zapsán do chybového ticketu, a ID/link ticketu je odeslán v hlášce klientovi. Pouze administrátor si může přečíst informace z chybového ticketu.

Je tu několik pravidel, na které je třeba myslet:

  • Rozdělíte-li modely na více souborů, modely ve stejné složce se vykonají v abecedním pořadí jmen souborů.
  • Všechny proměnné, které v modelech definujete, budou vidět abecedně následující modely, akce (funkce v kontroléru) a odpovídající pohled (view).
  • Modely můžete i uspořádat do podadresářů. Modely v podadresářích se vykonají jen podmíněně. Např. když uživatel požaduje adresu "/a/c/f", kde "a" je aplikace, "c" je kontrolér, a "f" je funkce (akce), vykonají se postupně tyto modely (pokud jste takové složky vytvořili):
applications/a/models/*.py
applications/a/models/c/*.py
applications/a/models/c/f/*.py
  • Požadovaný kontrolér je vykonán a pak je zavolána požadovaná funkce. Tím se myslí, že i top-level kód v kontroléru je vykonán při každém požadavku, směřovaném na dotyčný kontrolér.
  • Pohled (view) je použito jen v případě, když akce (funkce kontroléru) vrátí slovník (dictionary).
  • Když jste odpovídající pohled (view) ještě nevytvořili, Web2py se pokusí použít defaultní, generický pohled. Avšak defaultně jsou generické pohledy zakázány. Ovšem 'welcome' aplikace má řádek v /models/db.py, který generické pohledy povoluje, a sice pouze na localhost-u. Mohou být povoleny jen pro některé přípony nebo podle akce (přiřazením do response.generic_patterns). Obecně řečeno, generické pohledy jsou jen vývojářská pomůcka a neměly by se používat v produkčním prostředí. Chcete-li, aby některé akce generické pohledy použily, vypište tyto akce v response.generic_patterns (to je podrobněji diskutováno v kapitole 10 Služby).

Akce se může zachovat některým z následujících způsobů:

Vrátit řetězec

def index(): return 'data'

Vrátit slovník pro renderování pomocí pohledu:

def index(): return dict(key='value')

Vrátit slovník všech lokálních proměnných (pro renderování pomocí pohledu):

def index(): return locals()

Přesměrovat (redirect) uživatele na jinou stránku:

def index(): redirect(URL('other_action'))

Vrátit HTTP stránku jinou než "200 OK":

def index(): raise HTTP(404)

Vrátit helper (například FORM):

def index(): return FORM(INPUT(_name='test'))

(to se používá hlavně pro Ajax callbacky a komponenty, viz kapitola 12)

Vrací-li akce slovník (dictionary) mohou položky slovníku obsahovat kód, generovaný pomocí helperů, včetně formulářů založených na databázových tabulkách nebo factory-formulářů (které nejsou napojeny na tabulky, ale obsahují popis tabulky, podle nějž zobrazí prvky):

def index(): return dict(form=SQLFORM.factory(Field('name')).process())

(všechny formuláře, které Web2py generuje, používají postback, viz kapitola 3)

Dispatching (mapování URL)

url mapping
dispatching

Web2py mapuje URL ve tvaru:

http://127.0.0.1:8000/a/c/f.html

na funkci (akci) f() v kontroléru "c.py" v aplikaci "a". Chybí-li v adrese f, Web2py jako defaultní zavolá funkci index. Chybí-li c, Web2py jako defaultní zavolá "default.py" controller. A není-li uvedeno ani a, pak Web2py spoustí init aplikaci. Není-li init aplikace vytvořeny, Web2py se pokusí spustit welcome aplikaci. To je schematicky naznačeno na obrázku níže:

(Jméno defaultní aplikace, kontroléru a funkce (akce) může být změněno v souboru routes.py; viz Defaultní aplikace, kontrolér a funkce (akce) níže.

image

Defaultně každý nový požadavek zakládá session. Session cookie je předán klientskému prohlížeči, aby byla zachována aktuální session.

Přípona .html je nepovinná; .html se předpokládá implicitně. Přípona URL adresy určuje příponu pohledu (view), které bude formátovat výstup z funkce kontroléru f(). To umožňuje, aby stejný obsah byl prezentován v různých formátech (html, xml, json, rss, apod.).

Má-li v kontroléru definice funkce jakékoli parametry nebo když ji pojmenujete se dvěma podtržítky na začátku, nebude zveřejněna jako akce (dostupná pomocí URL) - může být jen volána z některé jiné funkce.

static files

Atypicky se chovají URL:

http://127.0.0.1:8000/a/static/filename

Není zde žádný kontrolér "static", ale Web2py toto interpretuje jako požadavek na soubor "filename" ze složky "a/static" v aplikaci "a".

PARTIAL CONTENT
IF_MODIFIED_SINCE
Během downloadu statických souborů Web2py nevytváří session, nezasílá cookie, ani nevykonává modely. Web2py vždy streamuje statické soubory po 1MB a zasílá PARTIAL CONTENT, když od klienta obdrží RANGE požadavek na další část souboru.

Web2py také podporuje IF_MODIFIED_SINCE protokol a nebude posílat soubor, jestliže je nezměněný už uložen v cache prohlížeče.

Odkazujete-li na audio nebo video soubor ve složce 'static' a nechcete, aby audio/video bylo streamováno přes media player, ale místo toho chcete, aby soubor byl nabídnut pro stažení, přidejte ?attachment do URL. Web2py pak přidá Content-Disposition hlavičku HTTP odpovědi s hodnotou "attachment". Například:

<a href="/app/static/my_audio_file.mp3?attachment">Download</a>

Když uživatel klikne na takový link, prohlížeč mu nabídne stažení celého MP3 souboru, místo aby jej začal streamovat. (Jak je vysvětleno níže, můžete také nastavit hlavičky HTTP odpovědi přímo, přiřazením slovníku se jmény hlaviček a jejich hodnotami do response.headers.)

request.application
request.controller
request.function
GET
POST
request.args
Web2py mapuje GET/POST požadavky:

http://127.0.0.1:8000/a/c/f.html/x/y/z?p=1&q=2

na funkci f v kontroléru "c.py" a v aplikaci a, a uloží URL parametry do request proměnné takto:

request.args = ['x', 'y', 'z']

a:

request.vars = {'p':1, 'q':2}

a:

request.application = 'a'
request.controller = 'c'
request.function = 'f'

Obojí, request.args[i] i request.args(i) může být použito pro získání i-tého prvku z request.args. Zatímco první vyvolá výjimku při chybném indexu, druhá varianta vrátí v takovém případě None.

request.url
request.url

obsahuje úplnou URL adresu aktuálního požadavku (bez GET proměnných).

request.ajax
request.cid

request.ajax

defaultně False, ale na True je nastaveno, když Web2py rozpozná, že akce byla zavolána jako Ajax požadavek.

Jestliže se jedná o Ajax požadavek a ten byl vyvolán Web2py komponentou, jméno komponenty naleznete v:

request.cid

Komponenty jsou podrobněji probírány v kapitole 12.

request.get_vars
request.post_vars
request.vars
Jestliže HTTP požadavek je GET, pak je shodně request.env.request_method nastavena na "GET"; podobně pro POST je nastaveno request.env.request_method "POST". URL query proměnné jsou uloženy do request.vars Storage slovníku (Storage je zděděná třída normálního python slovníku); jsou také uloženy v request.get_vars (v případě GET požadavku) nebo request.post_vars (jedná-li se o POST požadavek).

Web2py ukládá WSGI a Web2py proměnné prostředí do request.env, například:

request.env.path_info = 'a/c/f'

podobně HTTP hlavičky, například:

request.env.http_host = '127.0.0.1:8000'

Uveďme, že Web2py validuje všechna URL, aby zabránilo directory traversal útokům.

URL smějí obsahovat jen alfanumerické znaky, podtržítka a pomlčky; args může obsahovat také tečky /non-consecutive dots/. Mezery se nahradí podtržítky před validací. Není-li URL syntaxe validní, web2py vrátí chybové hlášení HTTP 400. [http:w,http:o] .

Když URL znamená požadavek na static soubor, Web2py jednoduše načte a vrátí (streamuje) požadovaný soubor.

Neznamená-li URL požadavek na static soubor, Web2py zpracuje požadavek tímto postupem:

  • Rozebere cookies.
  • Vytvoří prostředí pro vykonání funkce (akce).
  • Initializuje request, response, cache.
  • Otevře existující session nebo založí nové.
  • Vykoná odpovídající modely.
  • Vykoná z kontroléru požadovanou akci (funkci).
  • Vrátí-li akce slovník (dictionary), formátuje výstup pomocí odpovídajího pohledu (view).
  • Při úspěchu potvrdí (commit) otevřené transakce.
  • Uloží session.
  • Vrátí HTTP response.

Poznamenejme, že kontrolér a pohled (view) jsou vykonávány v různých kopiích téhož prostředí; proto pohled nevidí jména z kontroléru - vidí jména z modelů a proměnné, vrácené z akcí kontroléru formou slovníku (dictionary).

Dojde-li k výjimce (jiné než HTTP), Web2py udělá následující:

  • Uloží kontext volání (traceback) do chybového souboru a přiřadí mu číslo chybového ticketu.
  • Odvolá (rolls back) otevřené transakce.
  • Vrátí chybovou stránku s číslem ticketu a odkazem na něj.

Jestliže dojde k výjimce HTTP, je to považováno za zamýšlené chování (například HTTP redirect), a otevřené databázové transakce jsou potvrzeny (commit). Další chování pak záleží na samotné HTTP výjimce. HTTP exception třída není standardní exception Pythonu; je definována ve Web2py.

Knihovny

Web2py knihovny jsou zveřejněny pro uživatelskou aplikaci jako globální objekty. Například (request, response, session, cache), třídy (helpery, validátory, DAL API) a funkce (T a redirect).

Tyto objekty jsou definovány v následujících souborech jádra:

web2py.py
gluon/__init__.py    gluon/highlight.py   gluon/restricted.py  gluon/streamer.py
gluon/admin.py       gluon/html.py        gluon/rewrite.py     gluon/template.py
gluon/cache.py       gluon/http.py        gluon/rocket.py      gluon/storage.py
gluon/cfs.py         gluon/import_all.py  gluon/sanitizer.py   gluon/tools.py
gluon/compileapp.py  gluon/languages.py   gluon/serializers.py gluon/utils.py
gluon/contenttype.py gluon/main.py        gluon/settings.py    gluon/validators.py
gluon/dal.py         gluon/myregex.py     gluon/shell.py       gluon/widget.py
gluon/decoder.py     gluon/newcron.py     gluon/sql.py         gluon/winservice.py
gluon/fileutils.py   gluon/portalocker.py gluon/sqlhtml.py     gluon/xmlrpc.py
gluon/globals.py     gluon/reserved_sql_keywords.py

tar gzipovaná výchozí aplikace je

welcome.w2p

Je vytvořena po instalaci a přepsána při upgradu.

Když spustíte Web2py poprvé, vytvoří se 2 nové složky: deposit a applications. "welcome" aplikace je sbalena do "welcome.w2p" souboru, který pak slouží jako výchozí vzorová aplikace při vytvoření jakékoli další aplikace. Adresář deposit je používán jako dočasný při instalaci a odinstalaci aplikací.

web2py unit-testy jsou v

gluon/tests/

Dále jsou zde ovladače pro spolupráci s různými webovými servery:

cgihandler.py       # discouraged
gaehandler.py       # pro Google App Engine
fcgihandler.py      # pro FastCGI
wsgihandler.py      # pro WSGI
isapiwsgihandler.py # pro IIS
modpythonhandler.py # deprecated /zastaralé

("fcgihandler" volá "gluon/contrib/gateways/fcgi.py", který vyvinul Allan Saddi) a

anyserver.py

což je skript pro spolupráci s mnoha různými webovými servery, což je více popsáno v kapitole 13.

Jsou tu 3 vzorové soubory:

options_std.py
routes.example.py
router.example.py

První je volitelný konfigurační soubor, který je vhodný pro načtení pomocí -L volby při spuštění Web2py. Druhý je příklad souboru pro mapování URL. Bude se natahovat, jakmile jej přejmenujete na "routes.py". Třetí je alternativní syntaxe pro URL mapování a rovněž může být přejmenován nebo zkopírován na "routes.py".

Soubory

app.yaml
index.yaml
queue.yaml

jsou konfigurační soubory pro deployment (nasazení do produkčního prostředí) na Google App Engine. O nich se dozvíte více v kapitole Deployment Recipes a v dokumentaci Google.

Jsou přibaleny také přídavné knihovny, obvykle vytvořené třetími stranami:

feedparser[feedparser] od Marka Pilgrima pro čtení RSS a Atom feeds:

gluon/contrib/__init__.py
gluon/contrib/feedparser.py

markdown2[markdown2] od Trenta Micka pro wiki markup (značkovací jazyk):

gluon/contrib/markdown/__init__.py
gluon/contrib/markdown/markdown2.py

markmin markup:

gluon/contrib/markmin.py

fpdf od Mariano Reingarta pro generování PDF documentů:

gluon/contrib/fpdf

Není dokumentován v této knize, ale je udržován a dokumentován zde:

http://code.google.com/p/fpdf/

pysimplesoap je odlehčená implementace SOAP serveru od Mariano Reingarta:

gluon/contrib/pysimplesoap/

simplejsonrpc je odlehčený JSON-RPC klient, rovněž vytvořený Mariano Reingartem:

jsonrpc

gluon/contrib/simplejsonrpc.py

memcache[memcache] memcache API od Evana Martina:

gluon/contrib/memcache/__init__.py
gluon/contrib/memcache/memcache.py

redis_cache

redis
je modul pro uložení cache do redis databáze:

gluon/contrib/redis_cache.py

gql, a port DAL (databázové abstrakční vrstvy) pro Google App Engine:

gluon/contrib/gql.py

memdb, port DAL pro memcache /on top of memcache/:

gluon/contrib/memdb.py

gae_memcache API pro použití memcache na Google App Engine:

gluon/contrib/gae_memcache.py

pyrtf[pyrtf] pro sestavování Rich Text Format (RTF) dokumentů, který vyvinul Simon Cusack a revidoval Grant Edwards:

gluon/contrib/pyrtf
gluon/contrib/pyrtf/__init__.py
gluon/contrib/pyrtf/Constants.py
gluon/contrib/pyrtf/Elements.py
gluon/contrib/pyrtf/PropertySets.py
gluon/contrib/pyrtf/README
gluon/contrib/pyrtf/Renderer.py
gluon/contrib/pyrtf/Styles.py

PyRSS2Gen[pyrss2gen] vyvinutý Dalke Scientific Software ke generování RSS feedů:

gluon/contrib/rss2.py

simplejson[simplejson] od Boba Ippolita, standardní knihovna pro parsování a zapisování JSON objektů:

gluon/contrib/simplejson/__init__.py
gluon/contrib/simplejson/decoder.py
gluon/contrib/simplejson/encoder.py
gluon/contrib/simplejson/jsonfilter.py
gluon/contrib/simplejson/scanner.py

Google Wallet [googlewallet] poskytuje "pay now" buttony, které odkazují na platební nástroje Googlu:

gluon/contrib/google_wallet.py

Stripe.com [stripe] poskytuje jednoduchou API pro přijímání plateb kreditní kartou:

gluon/contrib/stripe.py

AuthorizeNet [authorizenet] API pro platby kreditní kartou pomocí sítě Authorize.net

gluon/contrib/AuthorizeNet.py

Dowcommerce [dowcommerce] další API pro zpracování plateb kreditní kartou:

gluon/contrib/DowCommerce.py

PAM[PAM] autentikační API, autorem je Chris AtLee:

gluon/contrib/pam.py

A Bayesian classifier pro plnění databáze náhodnými daty k testovacím účelům:

gluon/contrib/populate.py

Soubor pro spolupráci s taskbarem Windows, jestliže je Web2py spuštěno jako služba:

gluon/contrib/taskbar_widget.py

Volitelné login_methods a login_forms pro použití k autentifikaci:

gluon/contrib/login_methods/__init__.py
gluon/contrib/login_methods/basic_auth.py
gluon/contrib/login_methods/cas_auth.py
gluon/contrib/login_methods/dropbox_account.py
gluon/contrib/login_methods/email_auth.py
gluon/contrib/login_methods/extended_login_form.py
gluon/contrib/login_methods/gae_google_account.py
gluon/contrib/login_methods/ldap_auth.py
gluon/contrib/login_methods/linkedin_account.py
gluon/contrib/login_methods/loginza.py
gluon/contrib/login_methods/oauth10a_account.py
gluon/contrib/login_methods/oauth20_account.py
gluon/contrib/login_methods/openid_auth.py
gluon/contrib/login_methods/pam_auth.py
gluon/contrib/login_methods/rpx_account.py
gluon/contrib/login_methods/x509_auth.py

Web2py dále obsahuje adresář s užitečnými skripty

scripts/setup-web2py-fedora.sh
scripts/setup-web2py-ubuntu.sh
scripts/setup-web2py-nginx-uwsgi-ubuntu.sh
scripts/update-web2py.sh
scripts/make_min_web2py.py
...
scripts/sessions2trash.py
scripts/sync_languages.py
scripts/tickets2db.py
scripts/tickets2email.py
...
scripts/extract_mysql_models.py
scripts/extract_pgsql_models.py
...
scripts/access.wsgi
scripts/cpdb.py

První 3 jsou docela užitečné, protože usilují o úplnou instalaci a nastavení Web2py produkčního prostředí. Některé skripty jsou popsány v kapitole 14, ale ve všech najdete dokumentační řetězce, kter vysvětlují jejich účel a použití.

A nakonec Web2py obsahuje tyto soubory, potřebné pro sestavení binárních distribucí:

Makefile
setup_exe.py
setup_app.py

Jedná se o setup scripty pro py2exe a py2app, které jsou pouze potřeba pro sestavení binárních distribucí Web2py. Neměli byste je nikdy potřebovat.

V souhrnu Web2py knihovny poskytují následující funkcionalitu:

  • Mapují URL na volání funkcí.
  • Zajišťují přijímání a vracení parameterů přes HTTP.
  • Zajišťují validaci těchto parametrů.
  • Chrání aplikaci z hlediska většiny bezpečnostních rizik.
  • Usnadňují ukládání dat (databáze, session, cache, cookies).
  • Zajišťují překlad řetězců do jiných jazyků.
  • Generují HTML programově.
  • Generují SQL pomocí Databázové Abstrakční Vrstvy (DAL).
  • Generují Rich Text Format (RTF) výstup.
  • Generují Comma-Separated Value (CSV) výstup z databázových tabulek.
  • Generují Really Simple Syndication (RSS) feeds.
  • Generují JavaScript Object Notation (JSON) serializované řetězce pro Ajax.
  • Překládají wiki markup (Markdown) na HTML.
  • Zveřejňují XML-RPC webové služby.
  • Uploadují a downloadují velké soubory pomocí streamování.

Web2py aplikace obsahují další soubory, zejména JavaScript knihovny třetích stran, např. jQuery, calendar/datepicker, EditArea a nicEdit. Jejich autoři jsou uvedeni přímo v souborech.

Aplikace

Aplikace vytvořené ve Web2py sestávají z těchto částí:

  • modely popisují representaci dat jako databázových tabulek a relací mezi tabulkami.
  • kontroléry popisují aplikační logiku a postup zpracování.
  • pohledy (views) popisují, jak data mají být prezentována uživateli za pomoci HTML a JavaScriptu.
  • jazyky (languages) obsahují překladové řetězce textů do jiných jazyků.
  • static soubory jsou soubory, které není potřeba tvořit dynamicky (tj. obrázky, CSS styly, apod.).
  • ABOUT a README documenty.
  • errors ukládají informace o chybách za běhu aplikace.
  • sessions ukládají information, vztahující se k přístupu jednotlivých uživatelů k aplikaci.
  • databases obsahují SQLite databáze a metadata o vytváření tabulek.
  • cache ukládá cachované prvky aplikace.
  • modules jsou Python moduly, instalované jen pro konkrétní aplikaci.
  • private soubory jsou použity kontroléry, ale ne přímo vývojářem.
  • uploads soubory jsou použity modelem, ale ne přímo vývojářem, tj. jedná se o soubory, které uploadovali uživatelé.
  • tests adresář pro testy, fixtures a mocks.

Modely, pohledy (views), kontroléry, languages, a static soubory lze editovat v nezávislém vývojovém prostředí nebo programátorském editoru. Jsou také přístupné z webové administrace pro editaci/designování pomocí webu. ABOUT, README a chybové tickety jsou rovněž přístupné z administračního rozhraní pomocí odpovídajících položek nabídky. Sessions, cache, modules a private soubory jsou dostupné pro aplikaci, ale nejsou dostupné pro vývojáře z administračního rozhraní.

Vše je uspořádáno do přehledné struktury adresářů. Ta se replikuje pro každou vytvořenou nebo instalovanou Web2py aplikaci:

about
license
cache
controllers
databases
errors
languages
models
modules
private
session
static
tests
uploads
views
__init__.py

__init__.py  ABOUT        LICENSE    models    views
controllers  modules      private    tests     cron
cache        errors       upload     sessions  static

"__init__.py" je prázdný soubor, který je vyžadován, aby Python (a Web2py) mohlo najít a importovat moduly z adresáře modules.

admin aplikace poskytuje webový interface k Web2py aplikacím na serverovém souborovém systému. Web2py aplikace také mohou být vytvářeny a vyvíjeny mimo prostředí Web2py - není nutné používat admin interface z prohlížeče. Novou aplikaci lze vytvořit ručně tak, že replikujeme adresářovou strukturu existující aplikace do nové složky, např. "applications/newapp/" (a nebo jednoduše rozbalíme /untar/ welcome.w2p soubor do takového nového aplikačního adresáře. Soubory aplikace pak můžeme editovat v jakémkoli vhodném editoru a nepoužívat webové rozhraní aplikace admin.

API

Modely, kontroléry a pohledy (views) jsou vykonávány v prostředí, kde jsou už pro nás importovány následující často potřebné objekty:

Globální objekty:

request
response
session
cache

request, response, session, cache

Internacionalizace:

T
internationalization

T

Navigace:

redirect
HTTP

redirect, HTTP

Helpery:

helpers

XML, URL, BEAUTIFY

A, B, BODY, BR, 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, OL, UL, META, OBJECT, OPTION, P, PRE,
SCRIPT, OPTGROUP, SELECT, SPAN, STYLE,
TABLE, TAG, TD, TEXTAREA, TH, THEAD, TBODY, TFOOT,
TITLE, TR, TT, URL, XHTML, xmlescape, embed64

CAT, MARKMIN, MENU, ON

Formuláře a tabulky

SQLFORM (SQLFORM.factory, SQLFORM.grid, SQLFORM.smartgrid)

Validátory:

validators

CLEANUP, CRYPT, IS_ALPHANUMERIC, IS_DATE_IN_RANGE, IS_DATE,
IS_DATETIME_IN_RANGE, IS_DATETIME, IS_DECIMAL_IN_RANGE,
IS_EMAIL, IS_EMPTY_OR, IS_EXPR, IS_FLOAT_IN_RANGE, IS_IMAGE,
IS_IN_DB, IS_IN_SET, IS_INT_IN_RANGE, IS_IPV4, IS_LENGTH,
IS_LIST_OF, IS_LOWER, IS_MATCH, IS_EQUAL_TO, IS_NOT_EMPTY,
IS_NOT_IN_DB, IS_NULL_OR, IS_SLUG, IS_STRONG, IS_TIME,
IS_UPLOAD_FILENAME, IS_UPPER, IS_URL

Databáze:

DAL

DAL, Field

Kvůli zpětné kompatibilitě je dále k disspozici SQLDB=DAL a SQLField=Field. Doporučujeme ale používat novou syntaxi DAL a Field.

Další objekty a moduly jsou definovány v knihovnách, ale nejsou automaticky importovány, protože nebývají potřeba tak často.

Zásadní API entity ve Web2py prováděcím prostředí jsou request, response, session, cache, URL, HTTP, redirect a T - jsou probrány níže.

Několik objektů a funkcí, včetně Auth, Crud a Service, je definováno v "gluon/tools.py" a v případě potřeby je musíme importovat:

from gluon.tools import Auth, Crud, Service

Přístup k Web2py API z Python modulů

Vaše modely nebo kontroléry mohou importovat Python moduly, které mohou také potřebovat používat Web2py API. To si mohou zpřístupnit importem:

from gluon import *

Ve skutečnosti každý Python modul, i když není importován ve Web2py aplikaci, může takto importovat Web2py API. Je jen třeba, aby Web2py adresář byl uveden v sys.path.

Je tady ale jedna výjimka. Web2py definuje některé globální objekty (request, response, session, cache, T), které mohou existovat jen za existence (nebo napodobeníí) HTTP požadavku. Moduly k nim proto mohou přistupovat jen když jsou volány z aplikace. Z tohoto důvodu jsou umístěny do kontejneru current, což je lokální objekt vlákna. Zde je přílad:

Vytvořte modul "/myapp/modules/test.py" s obsahem:

from gluon import *
def ip(): return current.request.client

Nyní z některého kontroléru v aplikaci "myapp" můžete

import test
def index():
    return "Your ip is " + test.ip()

Všimněme si několika věcí:

  • import test hledá modul nejprve ve složce 'modules' aktuální aplikace, teprv potom ve složkách ze seznamu sys.path. App-level moduly vždy mají přednost před obecnými Python moduly. Umožňuje to různým aplikacím, aby využívaly různé verze modulů, aniž by došlo ke konfliktu.
  • Různí uživatelé mohou volat stejnou akci, např. index souběžně, což zavolá funkci v modulu, a přece zde nenastane konflikt, protože current.request je různý objekt v různých vláknech. Dejte však pozor a nepoužijte current.request mimo funkce a třídy (tj. v top level kódu modulu).
  • import test je zkratka pro from applications.appname.modules import test. Za pomoci této dlouhé syntaxe je možné importovat moduly z jiných aplikací.

Pro konzistenci s normálním chováním Pythonu Web2py defaultně neprovádí reload modulu ani když byl změněn. Ale toto chování můžete změnit. Pomocí funkce track_changes zapnete auto-reload modulů (typicky v modelu, a to dříve, než budete cokoli importovat):

from gluon.custom_import import track_changes; track_changes(True)

Od tohoto okamžiku importér modulů při každém importu vždy kontroluje, zda se zdrojový kód (.py) nezměnil. V případě změny natáhne modul v nové podobě. To se týká všech modulů, včetně Python modulů mimo Web2py. Takový mód je globální a ovlivní všechny aplikace současně. Vypnete jej stejným voláním s argumentem False. Aktuální nastavení módu zjistíte pomocí funkce is_tracking_changes(), rovněž z gluon.custom_import. Bez ohledu na mód reloadu importovaných modulů, změny přímo v modelech, kontrolérech a pohledech se ihned projeví.

Moduly, které importují current, mohou používat:

  • current.request
  • current.response
  • current.session
  • current.cache
  • current.T

a jakoukoli další proměnnou, kterou vaše aplikace uloží do current. Například model může udělat:

auth = Auth(db)
from gluon import current
current.auth = auth

a tím všechny moduly mají přístup ke

  • current.auth

current a import představují mocný mechanismus pro vytváření výkonných a v různých situacích použitelných modulů pro vaše aplikace.

Je třeba ale dávat pozor na jeden problém. Poté, co importujete pomocí from gluon import current, je zcela v pořádku používat current.request a další lokální objekty vlákna. Nikdy je ale nesmíte přiřadit globálním proměnným modulu, např.:

request = current.request # ŠPATNĚ! NEBEZPEČNÉ!

ani je nesmíte přiřadit atributům třídy:

class MyClass:
    request = current.request # ŠPATNĚ! NEBEZPEČNÉ!

Je to proto, že lokální objekt vlákna musí být založen během runtime. Naproti tomu globální proměnné jsou definovány jen jednou, když je model importován poprvé.

request

request
Storage
request.cookies
user_agent

request objekt je instancí Web2py třídy gluon.storage.Storage, která je odvozena z dict třídy Pythonu. Je to vlastně slovník, ale jeho prvky mohou být rovněž používány jako atributy:

request.vars

is the same as:

request['vars']

Také na rozdíl od slovníku (dictionary), když atribut (nebo chcete-li klíč) neexistuje, není vyvolána výjimka. Místo toho je vráceno None.

Někdy je šikovné vytvořit si Storage objekty pro vlastní účely. Můžete to udělat takto:

from gluon.storage import Storage
my_storage = Storage() # založit prázdný storage objekt
my_other_storage = Storage(dict(a=1, b=2)) # převést na storage existující slovník

request má následující prvky/atributy, některé jsou rovněž instance Storage třídy:

  • request.cookies: Cookie.SimpleCookie() objekt, který obsahuje cookies, předané s HTTP požadavkem. Funguje jako slovník (dictionary) cookies. Jednotlivá cookie je Morsel objekt /is a Morsel object/.
  • request.env: Storage objekt, který obsahuje proměnné prostředí, předané kontroléru, včetně promněnných z HTTP hlavičky požadavku a standardních WSGI parameterů. Všechny proměnné prostředí jsou převedeny na lowercase (malá písmena) a tečky jsou převedeny na podtržítka kvůli sjednocení a snadnějšímu pamatování.
  • request.application: jméno požadované aplikace (parsováno z request.env.path_info).
  • request.controller: jméno požadovaného kontroléru (parsováno z request.env.path_info).
  • request.function: jméno požadované funkce (parsováno z request.env.path_info).
  • request.extension: přípona požadované akce; defaultní přípona je "html". Jestliže akce (funkce kontroléru) vrátí slovník a neurčí-li jiné view (pomocí response.view=...), je přípona použita pro určení přípony pohledu (view), který zformátuje výstup - podobně jako .controller a .function určí adresář a jméno souboru použitého pohledu (parsováno z request.env.path_info).
  • request.folder: adresář aplikace. Např. pro "welcome" aplikaci request.folder je nastaven na absolutní cestu "/path/to/welcome". Pro přístup ke konkrétním souborům filesystemu v programech vždy používejte tuto proměnnou a funkci os.path.join. Ačkoli Web2py vždy používá absolutní cesty, není dobrý nápad měnit pracovní adresář, protože to není thread-safe (bezpečné pro ostatní vlákna).
  • request.now: datetime.datetime objekt, který obsahuje čas aktuálního požadavku.
  • request.utcnow: datetime.datetime objekt, který obsahuje čas aktuálního požadavku jako UTC (světový čas).
  • request.args: seznam (list) komponent URL cesty za /a/c/f (za jménem akce). Ekvivalentní výrazu request.env.path_info.split('/')[3:] (avšak bez GET .vars).
  • request.vars: gluon.storage.Storage objekt, který obsahuje HTTP GET a HTTP POST proměnné HTTP dotazu.
  • request.get_vars: gluon.storage.Storage jsou HTTP GET proměnné.
  • request.post_vars: gluon.storage.Storage jsou HTTP POST proměnné.
  • request.client: IP adresa klienta, získána z request.env.http_x_forwarded_for, pokud existuje, jinak z request.env.remote_addr.Ačkoli to je užitečné, nelze tomu zcela věřit, protože http_x_forwarded_for může být podvrženo /can be spoofed/.
  • request.is_local: True, když klientem je localhost, False v ostatních případech. Mělo by pracovat i za proxy, pokud proxy podporuje http_x_forwarded_for.
  • request.is_https: True při použití HTTPS protokolu, False v ostatních případech.
  • request.body: read-only file stream, který obsahuje tělo (body) HTTP požadavku. Odtud jsou automaticky parsovány request.post_vars. request.post_vars lze číst pomocí request.body.read().
  • request.ajax je True, byla-li funkce zavolána jako Ajax požadavek.
  • request.cid je id komponenty, která generovala Ajax požadavek. O komponentách si přečtete více v kapitole 12.
  • request.restful Jedná se o nový a velmi užitečný dekorátor, kterým lze měnit defaultní chování Web2py akcí separováním GET/POST/PUSH/DELETE požadavků. Některé podrobnosti diskutujeme v kapitole 10.
  • request.user_agent() parsuje user_agent pole od klienta a vrací informace formou slovníku (dictionary). Je užitečné při rozpoznávání mobilních zařízení. Používá "gluon/contrib/user_agent_parser.py", který vytvořili Ross Peoples. Jak pracuje, uvidíte dobře, když do view vložíte:
{{=BEAUTIFY(request.user_agent())}}
  • request.wsgi je hook, který vám umožní volat WSGI aplikace třetích stran zevnitř Web2py akcí.

Obsahuje tyto položky:

  • request.wsgi.environ
  • request.wsgi.start_response
  • request.wsgi.middleware

Jejich použití je ukázáno na příkladu na konci této kapitoly.

Příklad: Na obvyklém systému následující volání:

http://127.0.0.1:8000/examples/default/status/x/y/z?p=1&q=2

sestaví takovýto request objekt:

request
env

proměnnáhodnota
request.applicationexamples
request.controllerdefault
request.functionstatus
request.extensionhtml
request.viewstatus
request.folderapplications/examples/
request.args['x', 'y', 'z']
request.vars<Storage {'p': 1, 'q': 2}>
request.get_vars<Storage {'p': 1, 'q': 2}>
request.post_vars<Storage {}>
request.is_localFalse
request.is_httpsFalse
request.ajaxFalse
request.cidNone
request.wsgi(hook)
request.env.content_length0
request.env.content_type
request.env.http_accepttext/xml,text/html;
request.env.http_accept_encodinggzip, deflate
request.env.http_accept_languageen
request.env.http_cookiesession_id_examples=127.0.0.1.119725
request.env.http_host127.0.0.1:8000
request.env.http_max_forwards10
request.env.http_refererhttp://web2py.com/
request.env.http_user_agentMozilla/5.0
request.env.http_via1.1 web2py.com
request.env.http_x_forwarded_for76.224.34.5
request.env.http_x_forwarded_hostweb2py.com
request.env.http_x_forwarded_server127.0.0.1
request.env.path_info/examples/simple_examples/status
request.env.query_stringremote_addr:127.0.0.1
request.env.request_methodGET
request.env.script_name
request.env.server_name127.0.0.1
request.env.server_port8000
request.env.server_protocolHTTP/1.1
request.env.web2py_path/Users/mdipierro/web2py
request.env.web2py_versionVersion 1.99.1
request.env.web2py_runtime_gae(definováno jen když pracuje na GAE)
request.env.wsgi_errors<open file, mode 'w' at >
request.env.wsgi_input
request.env.wsgi_multiprocessFalse
request.env.wsgi_multithreadTrue
request.env.wsgi_run_onceFalse
request.env.wsgi_url_schemehttp
request.env.wsgi_version10

Které proměnné prostředí jsou aktuálně definovány je ovlivněno použitým web serverem. Zde předpokládáme vestavěný Rocket wsgi server. Sada proměnných se příliš neliší při použití serveru Apache.

request.env.http_* proměnné jsou parsovány z HTTP hlavičky požadavku.

request.env.web2py_* proměnné nejsou získány z prostředí webového serveru, ale vytvoří je Web2py, pro případ, že baše aplikace potřebuje znát umístění a verzi Web2py a zda se nacházíme na GAE - Google App Engine (protože mohou v tom případě být potřebné specifické optimalizace).

Také zmiňme request.env.wsgi_* proměnné. Jsou specifické podle wsgi adaptéru.

response

response
response.body
response.cookies
response.download
response.files
response.flash
response.headers
response.meta
response.menu
response.postprocessing
response.render
response.status
response.stream
response.subtitle
response.title
response.toolbar
response.view
response.delimiters
response.js
response.write
response.include_files
response.include_meta
response.optimize_css
response.optimize_js

response je také instancí Storage třídy. Obsahuje následující:

response.body: StringIO objekt, do nějž Web2py zapisuje tělo výstupní stránky. NIKDY NEMĚŇTE TUTO PROMĚNNOU.

response.cookies: podobné request.cookies, ale obsahují cookies, právě posílané serverem klientovi. Session cookie je sestaveno a zpracováno automaticky.

response.download(request, db): metoda, použitá pro implementaci funkce kontroléru pro download uploadovaných souborů. request.download očekává, že poslední arg v request.args bude kódované jméno souboru (tj. jméno souboru, generované při uploadu a uložené v upload poli); z jména souboru z args získá jméno tabulky a upload pole v tabulce a také originální jméno souboru. response.download má dva volitelné parametry: chunk_size nastavuje velikost v bytech pro chunked streaming (default 64K), a attachments, který určuje, zda soubor má být chápán jako attachment nebo ne (default True). Poznamenejme, že response.download je specificky určeno pro download souborů asociovaných s upload poli databáze. Použijte response.stream (viz níže) pro jiné typy downloadu souborů nebo streamování. Také uveďme, že není nutné používat response.download pro přístup k souborům uploadovaným do /static složky -- k těmto souborům se může (a obecně by se mělo) přistupovat přímo pomocí URL (např., /app/static/files/myfile.pdf).

response.files: seznam .css, .js, .coffee, a .less souborů, které stránka požaduje. Do hlavičky standardního rámcového pohledu "layout.html" je automaticky přidá "web2py_ajax.html". Chcete-li přidat další CSS, JS, COFFEE, nebo LESS soubor, nejlépe ho připojte (append) k tomuto seznamu. Duplicity jsou ošetřeny; je důležité pořadí souborů.

response.include_files() je metoda, která generuje tagy html hlavičky pro připojení všech response.files (volá se ve "views/web2py_ajax.html").

response.flash: volitelný parametr, který můžete nastavit. Normálně se používá pro jednoduchou informaci uživatele, že se něco přihodilo.

response.headers: slovník (dict) pro hlavičky HTTP odpovědi. Web2py některé hlavičky nastaví defaultně: "Content-Length", "Content-Type", a "X-Powered-By" (= web2py). Web2py také nastaví "Cache-Control", "Expires", a "Pragma" hlavičky k zabránění cachování na straně klienta (s výjimkou požadavků na static soubory, pro něž je cachování na straně klienta dovoleno. Hlavičky, které nastaví Web2py, můžete přepsat nebo odstranit, a další hlavičky můžete přidat (např.: response.headers['Cache-Control'] = 'private').

response.menu: volitelný parametr, který může být připojen v pohledu. Normálně je použit k předání navigačního menu. Může být formátován pomocí MENU() helperu.

response.meta: storage object (tedy odvozený ze slovníku (dict)), který obsahuje volitelné meta informace jako response.meta.author, .description, a/nebo .keywords. Obsah každé meta proměnné je automaticky vložen do odpovídající META značky kódem ve "views/web2py_ajax.html", který je defaultně volán z "views/layout.html".

response.include_meta() generuje řetězec, který připojí všechny serializované response.meta hlavičky (volá se z "views/web2py_ajax.html").

response.postprocessing: seznam funkcí, defaultně prázdný. Tyto funkce lze použít pro filtrování response objektu na výstupu z akce (z funkce kontroléru), dříve než je výstup zformátován pomocí pohledu (view). To může být použito, chcete-li implementovat podporu jiného template jazyka než je ve Web2py standardní.

response.render(view, vars): Metoda, kterou lze použít pro explicitní volání pohledu (view) zevnitř kontroléru. view je volitelný parametr se jménem souboru pohledu, vars je slovník (dictionary) hodnot, předaných do view.

response.session_file: file stream, který obsahuje session.

response.session_file_name: jméno souboru, do nějž se uloží session.

response.session_id: id aktuální sessiony. Je určováno automaticky. NIKDY NEMĚŇTE TUTO PROMĚNNOU.

response.session_id_name: jméno session cookie pro tuto aplikaci. NIKDY NEMĚŇTE TUTO PROMĚNNOU.

response.status: HTTP status kód - integer, předané do odpovědi (response). Default je 200 (OK).

response.stream(file, chunk_size, request=request, attachment=False, filename=None, headers=None): Jestliže kontrolér toto vrátí, Web2py bude streamovat obsah souboru klientovi v blocích o velikosti chunk_size. request parametr je potřeba pro zahájení v HTTP hlavičce. file by měla být cesta k souboru (ačkoli kvůli zpětné kompatibilitě to může být i ovetřený file objekt - což se ale nedoporučuje). Jak bylo řečeno výše, response.download je namísto toho potřeba použít pro stažení souborů, uložených pomocí upload polí. response.stream může být použit v ostatních případech, jako je např. vrácení dočasného souboru nebo StringIO objektu, který sestaví kontrolér. Jestliže attachment je True, Content-Disposition hlavička bude nastavena na "attachment" a je-li uvedeno také filename, bude rovněž přidáno do Content-Disposition hlavičky (když attachment==True). Jestliže zatím nejsou v response.headers, nastaví se následující hlavičky automaticky: Content-Type, Content-Length, Cache-Control, Pragma a Last-Modified (poslední 3 povolují browseru cachování tohoto souboru). Chcete-li automaticky generované hlavičky přepsat, jednoduše je nastavte v response.headers předtím, než zavoláte response.stream.

response.subtitle: volitelný parametr, který může být vypsán ve view. Obvykle obsahuje podtitulek stránky. Např. ve welcome aplikaci se nastavuje v menu.py modelu.

response.title: volitelný parametr, který může být vypsán ve view. Měl by obsahovat titulek stránky a měl by být zformátován do <title> značky. Např. ve welcome aplikaci se nastavuje v menu.py modelu a v rámcovém pohledu layout.html formátuje do title značky.

response.toolbar: funkce, která vám umožní vložit do stránky toolbar, vhodný pro ladící účely: {{=response.toolbar()}}. Toolbar zobrazuje request, response, session proměnné a databázová připojení, tabulky a statistiku provedených databázových dotazů včetně doby provedení dotazu.

response._vars: tato proměnná je k dispozici jen v pohledu (view), nikoli v kontroléru. Obsahuje hodnotu, kterou akce kontroléru předala do view.

response.optimize_css: lze nastavit na "concat,minify,inline" pro concatenate, minify a inline CSS souborů.

response.optimize_js: lze nastavit na "concat,minify,inline" pro concatenate, minify a inline souborů JavaScriptu.

response.view: jméno pohledu (view šablony), který bude renderovat (formátovat) výslednou stránku. Jestliže nenastavíte jinak, nastaví se defaultně na:

"%s/%s.%s" % (request.controller, request.function, request.extension)

nebo, když předchozí neexistuje a jsou povoleny defaultní (generické) pohledy, na:

"generic.%s" % (request.extension)

Změňte v akci kontroléru hodnotu této proměnné, jestliže chcete pro zformátování použít jiný než defaultní pohled (view).

response.delimiters defaultně ('{{','}}'). Umožňuje vám nastavit jiné než standardní oddělovače pro Python kód v pohledech.

response.xmlrpc(request, methods): vrátí-li kontrolér toto, funkce zveřejňuje metody methods pomocí XML-RPC[xmlrpc] . Tato funkce je zastaralá (deprecated), protože je k dispozici lepší mechanismus, který popisujeme v kapitole 10.

response.write(text): metoda pro zápis textu do výstupu těla stránky (volá se během sestavování automaticky; obvykle ji nepotřebujete explicitně volat).

response.js může obsahovat kód Javascriptu. Tento kód se vykoná jedině a pouze tehdy, když odpověď (response) přijme Web2py komponenta - to je diskutováno v kapitole 12.

Protože response je gluon.storage.Storage objekt, můžete jej použít k uložení dalších hodnot, které byste potřebovali předat do view. Ačkoli technické omezení zde není, doporučujeme toto využívat jedině pro hodnoty, které se předávají všem stránkám a tedy formátují v rámcovém pohledu "layout.html". Hodnoty, specifické pro dílčí stránky, předávejte standardním mechanismem (tedy jako slovník (dictionary), předaný návratovou hodnotou z akce kontroléru).

Nejlépe se omezte jen na proměnné, vypsané zde:

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

protože to vám usnadní zaměnit standardní "layout.html" soubor (hlavní rámcový pohled), který je dodáván spolu s Web2py, za jiný, který samozřejmě musí používat stejnou sadu proměnných.

Staré verze Web2py používaly response.author místo response.meta.author a podobně pro další meta atributy.

session

session
session.connect
session.forget
session.secure
session je další instancí Storage třídy. Cokoli je uloženo do session, např.:

session.myvariable = "hello"

lze získat zpět při pozdějších přístupech:

a = session.myvariable

a to dokud je kód vykonáván ve stejné sessioně - stejným uživatelem ze stejného prohlížeče, dokud sessioně nevyprší platnost (pokud uživatel nesmaže session cookies). Protože session je Storage objekt, přístup k neexistujícímu atributu/klíči nevyvolá výjimku, ale vrátí None.

session objekt má 3 důležité metody. Jedna z nich je forget:

session.forget(response)

Tím Web2py řeknete, aby neukládalo session. To by mělo být použito v kontrolérech, které session nevyužívají (nepotřebují sledovat uživatelskou aktivitu během více přístupů) a zároveň jsou jejich akce volány poměrně často. session.forget() zabrání zápisu session souboru bez ohledu na to, zda byl modifikován nebo ne. session.forget(response) navíc odemkne a uzavře session soubor. Obvykle tuto metodu volat nemusíte. Ale jestliže stránka zasílá více současných Ajax požadavků, je vhodné v akcích, volaných Ajaxem, zavolat session.forget(response) (za předpokladu, že tyto akce nepotřebují session používat). V opačném případě totiž každá Ajax akce bude muset čekat, až skončí předchozí a odemkne session soubor. To zpomalí načtení stránky. Poznamenejme, že sessiony nejsou zamykány, jestliže je přesměrujeme do databáze místo do souboru.

Další metoda je:

session.secure()

která Web2py řekne, aby nastavila session cookie jako bezpečné cookie. To je vhodné nastavit, když komunikujeme přes https. Nastavením session cookie jako bezpečného (secure), server žádá browser, aby nezasílal session cookie zpět v případě nezabezpečeného spojení.

Další metoda je connect:

session.connect(request, response, db, masterapp=None)

kde db je jméno otevřeného databázového spojení (jak jej vrátí DAL()). Tím řeknete Web2py, aby ukládalo sessiony do databáze místo do filesystému. session.connect musí následovat po db=DAL(...), ale dříve, než je využita další logika, která vyžaduje sessiony, např. před použitím Auth.

Web2py vytvoří tabulku:

db.define_table('web2py_session',
                 Field('locked', 'boolean', default=False),
                 Field('client_ip'),
                 Field('created_datetime', 'datetime', default=now),
                 Field('modified_datetime', 'datetime'),
                 Field('unique_key'),
                 Field('session_data', 'text'))

a uloží cPickle serializované sessiony do session_data pole.

Volba masterapp=None (defaultní nastavení) říká Web2py, aby zkusilo získat existující sessionu pro aplikaci, jejíž jméno je v request.application.

Chcete-li aby 2 nebo více aplikací sdílely sessiony, nastavte masterapp na jednu z nich, kterou zvolíte jako hlavní aplikaci.

Můžete zjistit stav vaší aplikace výpisem request, session a response systémových proměnných. Např. si k tomu účelu vytvoříte speciální akci:

def status():
    return dict(request=request, session=session, response=response)

Snadnější je použít ve view připravený toolbar: {{=response.toolbar()}}

Oddělení session

Ukládáte-li sessiony do souborového systému a vzniká-li jich mnoho, přístup k souborovému systému bude zpomalovat aplikaci. Jedno z řešení je následující:

session.connect(request, response, separate=True)

Nastavením separate=True Web2py začne ukládat sessiony ne přímo do "sessions/" složky, ale do jejích podadresářů, které vytvoří automaticky. Sessiony se stejným prefixem budou sdílet stejnou podsložku. Opět je třeba toto zavolat dříve, než se dále pracuje se session.

cache

cache
cache.ram
cache.disk
cache další globální objekt, který je přístupný ve Web2py prováděcím prostředí. Má 2 atributy:

  • cache.ram: aplikační cache v operační paměti.
  • cache.disk: aplikační cache na disku.

cache je volatelná (callable), a to umožňuje použít ji jako dekorátor pro cachované akce nebo pohledy.

Následující příklad cachuje time.ctime() funkci v paměti:

def cache_in_ram():
    import time
    t = cache.ram('time', lambda: time.ctime(), time_expire=5)
    return dict(time=t, link=A('click me', _href=request.url))

Výstup z lambda: time.ctime() je cachován v paměti po dobu 5 sekund. Řetězec 'time' je použit jako klíč pro cache.

Následující příklad cachuje time.ctime() funkci na disku:

def cache_on_disk():
    import time
    t = cache.disk('time', lambda: time.ctime(), time_expire=5)
    return dict(time=t, link=A('click me', _href=request.url))

Výstup z lambda: time.ctime() je cachován na disku (za pomoci shelve modulu) po dobu 5 sekund.

Poznamenejme, že druhý argument pro cache.ram nebo cache.disk musí být funkce nebo volatelný (callable) objekt. Chcete-li cachovat existující objekt místo výstupu z funkce, jednoduše jej vraťte jako výstup z lambda funkce:

cache.ram('myobject', lambda: myobject, time_expire=60*60*24)

Následující příklad cachuje time.ctime() funkci do paměti a na disk současně:

def cache_in_ram_and_disk():
    import time
    t = cache.ram('time', lambda: cache.disk('time',
                       lambda: time.ctime(), time_expire=5),
                       time_expire=5)
    return dict(time=t, link=A('click me', _href=request.url))

Výstup z lambda: time.ctime() je cachován na disku (pomocí shelve modulu) a pak v paměti po dobu 5 sekund. Web2py se podívá nejprve do paměti a pak případně na disk. Nenajde-li údaj nikde, vykoná lambda: time.ctime() a aktualizuje cache pro příště. Tato technika je užitečná ve víceprocesovém prostředí. Obě doby uložení mohou být odlišné.

Následující příklad cachuje v paměti výstup z akce kontroléru (ale nikoli pohled):

cache controller
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_in_ram():
    import time
    t = time.ctime()
    return dict(time=t, link=A('click me', _href=request.url))

Slovník, který vrátí cache_controller_in_ram je cachován v paměti po dobu 5 vteřin. Poznamenejme, že výsledek výběru z databáze nemůže být cachován dokud jej neserializujeme. Lepší cestou je výběr z databáze cachovat přímo pomocí parametru select() metody - jedná se o parametr cache.

Následující příklad cachuje na disku výstup z akce kontroléru (ale nikoli pohled):

@cache(request.env.path_info, time_expire=5, cache_model=cache.disk)
def cache_controller_on_disk():
    import time
    t = time.ctime()
    return dict(time=t, link=A('click to reload',
                              _href=request.url))

Slovník, který vrátí cache_controller_in_ram je cachován na disku po dobu 5 vteřin. Připomeňmme si znovu, že Web2py nemůže cachovat slovník, v němž jsou neserializovatelné (un-pickleable) objekty.

Je možné cahcvat i pohled (view). Trik spočívá v renderování pohledu uvnitř akce kontroléru, takže výstup z akce už nevrací slovník (dictionary) a nevolá tím pohled, ale vrátí přímo výsledný řetězec. Ten získáme pomocí response.render(d), kde d je slovník, který bychom normálně předali pro formátování do pohledu. Následující příklad cachuje výstup z akce v paměti včetně souvisejícího pohledu:

cache view
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_and_view():
    import time
    t = time.ctime()
    d = dict(time=t, link=A('click to reload', _href=request.url))
    return response.render(d)

response.render(d) vrací pohled jako sestavený řetězec, který je nyní cachován po dobu 5 vteřin. Toto je nejlepší a nejrychlejší způsob cachování.

Poznamenejme, že time_expire porovnává aktuální čas a čas, kdy byl cachovaný objekt uložen do paměti. Neovlivňuje budoucí požadavky (které mohou mít odlišný time_expire). Takže time_expire se určuje dynamicky v okamžiku požadavku, nikoli v okamžiku, kdy do paměti ukládáme. Například:

message = cache.ram('message', lambda: 'Hello', time_expire=5)

Nyní předpokládejme, že následující volání provedeme 10 vteřin po předchozím:

message = cache.ram('message', lambda: 'Goodbye', time_expire=20)

Protože time_expire ve druhém volání je 20 sekund, ale jen 10 sekund uplynulo od uložení message, obdržíme hodnotu "Hello", získanou z cache, a nikoli aktualizovanou hodnotu "Goodbye". time_expire hodnota 5 sekund z prvního volání na to nemá vliv.

Nastavení time_expire=0 (nebo na zápornou hodnotu) vynutí, aby chachovaný objekt byl refreshován (protože čas uplynulý od posledního uložení je vždy > 0), kdežto nastavení time_expire=None vynutí načtení cachované hodnoty bez ohledu na uplynulý čas (jestliže by time_expire při volání bylo vždy None, cachovaný objekt by nikdy neexpiroval).

Můžete odstranit hodnoty z cache:

cache clear
cache.ram.clear(regex='...')

kde regex je regulární výraz pro popis všech položek, které chcete z cache odstranit. Můžete také smazat jednotlivou položku takto:

cache.ram(key, None)

kde key je klíč cachované položky.

Je také možné definovat jiné cachovací mechanismy, jako je např. memcache. Memcache je dostupná pomocí gluon.contrib.memcache a je podrobněji popsána v kapitole 14.

Buďte opatrní při využívání cache, protože cachování se děje na úrovni aplikace, nikoli uživatele. Potřebujete-li cachovat uživatelsky specifický obsah, musíte např. do cache klíče přidat id uživatele (auth.user.id).

URL

URL

URL funkce je jedna z nejdůležitějších ve Web2py. Sestavuje URL cesty pro akce nebo static soubory.

Tady je příklad:

URL('f')

je mapováno na

/[application]/[controller]/f

Výstup z URL funkce závisí na aktuální aplikaci, aktuálním kontroléru a na dalších parametrech. Web2py podporuje URL mapování a reverzní URL mapování. URL mapování vám dovoluje redefinovat formát externích URL adres. Použijete-li URL funkci pro sestavení všech interních URL adres, pak změny nebo rozšíření URL mapování nezpůsobí přerušení odkazů (broken links) uvnitř Web2py aplikace.

Můžete do URL funkce uvést další parametry, např. extra položky v URL cestě (args) a proměnné URL dotazu (vars):

URL('f', args=['x', 'y'], vars=dict(z='t'))

je mapováno na

/[application]/[controller]/f/x/y?z=t

args atributy jsou automaticky parsovány, dekódovány a nakonec uloženy do request.args. Podobně vars po jejich parsování a dekódování jsou uloženy do request.vars. args a vars poskytují základní mechanismus, pomocí kterého si Web2py vyměňuje informace s klientským browserem.

Jestliže args obsahuje jedinou položku, můžete ji uvést normálně - nikoli jako seznam (list).

URL funkcí můžete sestavovat URL adresy akcí v jiných kontrolérech a aplikacích takto:

URL('a', 'c', 'f', args=['x', 'y'], vars=dict(z='t'))

je mapováno na

/a/c/f/x/y?z=t

Můžete také použít pojmenované argumenty k zadání aplikace, kontroléru a funkce:

URL(a='a', c='c', f='f')

Chybí-li jméno aplikace, předpokládá se aktuální aplikace.

URL('c', 'f')

Chybí-li jméno kontroléru, předpokládá se aktuální kontrolér.

URL('f')

Místo předání jména funkce kontroléru je také možné předat funkci samotnou:

URL(f)

Z důvodů výše uvedených byste vždy měli URL funkci používat i pro sestavení URL adres static souborů, tj. souborů z adresáře static. Web2py poskytuje virtuální (zdánlivý) 'static' kontrolér, jehož starostí je odkazovat na soubory ve static složce, určovat jejich content-type a streamovat soubor klientovi. Následující příklad sestaví URL pro static soubor "image.png":

URL('static', 'image.png')

to je mapováno na

/[application]/static/image.png

Máte-li static soubor v podadresáři static složky, můžete uvést cestu jako součáast jména souboru. Např. abyste vygenerovali:

/[application]/static/images/icons/arrow.png

lze uvést:

URL('static', 'images/icons/arrow.png')

Není třeba kódovat/escapovat args a vars argumenty; to je provedeno automaticky.

Defaultně bude přípona z aktuálního požadavku (kterou lze nalézt v request.extension) přidána k jménu funkce, pokud request.extension není html (default). To je možné potlačit explicitním uvedením přípony jako součást jména funkce: URL(f='name.ext') a nebo pomocí extension argumentu:

URL(..., extension='css')

Nebo lze defaultní příponu potlačit:

URL(..., extension=False)

Absolutní URL

Defaultně URL funkce generuje relativní URL adresy. Ale můžete sestavovat také absolutní, když zadáte scheme a host argumenty (to je např. užitečné, když vkládáte URL do emailu):

URL(..., scheme='http', host='www.mysite.com')

Můžete automaticky vložit scheme a host aktuálního požadavku použitím argumentu True:

URL(..., scheme=True, host=True)

URL funkce má ještě port argument pro zadání serverového portu, je-li to potřeba.

Digitálně podepsaná URL

digitally signed URL

Když sestavujete URL, máte možnost je digitálně podepsat. Tím přidáte _signature GET proměnnou, která může být ověřena serverem. Lze to udělat dvěma způsoby.

Můžete do URL funkce předat tyto argumenty:

  • hmac_key: klíč pro podepsání URL (řetězec)
  • salt: volitelný řetězec pro pozměnění dat /salt the data/ před podepsáním
  • hash_vars: volitelný řetězec jmen proměnných z URL dotazovacího řetězce (z GET proměnných), které se přidají do podpisu. Může být také uvedeno True (default) k zahrnutí všech GET proměnných nebo False k jejich vynechání.

Tady je příklad použití:

KEY = 'mykey'

def one():
    return dict(link=URL('two', vars=dict(a=123), hmac_key=KEY))

def two():
    if not URL.verify(request, hmac_key=KEY): raise HTTP(403)
    # do something
    return locals()

To udělá akci two přístupnou jedině pomocí digitálně podepsané URL adresy. Digitálně podepsaná adresa vypadá nějak takto:

'/welcome/default/two?a=123&_signature=4981bc70e13866bb60e52a09073560ae822224e9'

Poznamenejme, že digitální podpis je ověřen pomocí URL.verify funkce, která rovněž dostane argumenty hmac_key, salt a/nebo hash_vars, popsané výše, a jejich hodnoty musí být samozřejmě identické s hodnotami, které použila URL funkce pro podpis.

Druhé, více sofistikované, ale méně časté použití digitálních podepsaných URL je ve spolupráci s Auth. Nejlépe to vysvětlí příklad:

@auth.requires_login()
def one():
    return dict(link=URL('two', vars=dict(a=123), user_signature=True)

@auth.requires_signature()
def two():
    # do something
    return locals()

V tomto případě je hmac_key automaticky generováno a sdíleno v session. To umožní akci two povolení přístupu jen z odkazu, generovaného akcí one. Je-li odkaz sestaven a podepsán, je platný; jinak není. Podvrhne-li odkaz jiný uživatel, je odkaz neplatný.

Je dobrou praxí vždy digitálně podepisovat Ajax callbacky. Když využijete Web2py LOAD funkci, má pro tento účel user_signature argument:

{{=LOAD('default', 'two', vars=dict(a=123), ajax=True, user_signature=True)}}

HTTP a redirect

HTTP
redirect

Web2py definuje jedinou novou výjimku, nazvanou HTTP. Tuto výjimku můžete vyvolat kdekoli z modelu, kontroléru nebo pohledu pomocí příkazu:

raise HTTP(400, "můj text")

Tím předáte řízení z uživatelského kódu pryč, zpět do Web2py, a bude vrácena HTTP odpověď podobná této:

HTTP/1.1 400 BAD REQUEST
Date: Sat, 05 Jul 2008 19:36:22 GMT
Server: Rocket WSGI Server
Content-Type: text/html
Via: 1.1 127.0.0.1:8000
Connection: close
Transfer-Encoding: chunked

můj text

První argument HTTP je HTTP status kód. Druhý argument je řetězec, který se předá jako tělo odpovědi. Další pojmenované argumenty se mohou případně použít pro sestavení HTTP hlavičky. Například:

raise HTTP(400, 'my message', test='hello')

vygeneruje:

HTTP/1.1 400 BAD REQUEST
Date: Sat, 05 Jul 2008 19:36:22 GMT
Server: Rocket WSGI Server
Content-Type: text/html
Via: 1.1 127.0.0.1:8000
Connection: close
Transfer-Encoding: chunked
test: hello

můj text

Chcete-li zabránit potvrzení (commit) databázové transakce, odvolejte změny před vyvoláním této výjimky (db.rollback()).

Jakákoli jiná výjimka než HTTP způsobí, že Web2py odvolá automaticky změny v každé otevřené databázové transakci, sestaví log z kontextu volání, vystaví chybový ticket pro uživatele, a vrátí standardní chybovou stránku.

Znamená to, že jedině HTTP výjimka může být použita pro předávání řízení mezi stránkami. Ostatní výjimky musí být odchyceny aplikací, jinak je Web2py reportuje chybovým ticketem.

Příkaz:

redirect('http://www.web2py.com')

není nic jiného než zkratka pro:

raise HTTP(303,
           'Jste přesměrován <a href="%s">sem</a>' % location,
           Location='http://www.web2py.com')

Pojmenované argumenty HTTP volání se převedou na direktivy HTTP hlavičky, v tomto případě na cílovou adresu. redirect má druhý volitelný argument, což je HTTP stavový kód pro přesměrování (303 defaultně). Můžete změnit toto číslo na 307 pro dočasné přesměrování nebo na 301 pro trvalé přesměrování.

Nejčastější způsob využití přesměrování je přechod na jinou stránku téže aplikace (a případné přidání parametrů):

redirect(URL('index', args=(1,2,3), vars=dict(a='b')))

V kapitole 12 probíráme Web2py komponenty. Ty generují Ajax požadavky na Web2py akce. Jestliže volaná akce provede přesměrování, můžete mít buď na mysli, že Ajax požadavek následuje přesměrování, nebo že celá stránka následuje přesměrování. Ve druhém případě použijte parametr:

redirect(...,type='auto')

T a internacionalizace

T
internationalization

Objekt T je převaděčem mezi jazyky. Vytvoří jednu globální instanci Web2py třídy gluon.language.translator. Všechny řetězcové konstanty by měly používat T, například:

a = T("hello world")

Řetězce, označené pomocí T rozpoznává Web2py jako řetězce, které potřebují překlad do cílového jazyka aplikace a přeloží je, když je kód vykonán. Jestliže řetězec není konstanta, ale proměnná, je přidán do překladové tabulky během runtimu (to ale neplatí na GAE) a je možné jej přeložit později.

Volání T objektu může také obsahovat dosazované proměnné a je možno více různých syntaxí:

a = T("hello %s", ('Tim',))
a = T("hello %(name)s", dict(name='Tim'))
a = T("hello %s") % ('Tim',)
a = T("hello %(name)s") % dict(name='Tim')

Syntaxe v dolní polovině je doporučená, protože zjednodušuje překlad. První řetězec je přeložen podle požadovaného jazykového souboru a jméno je pak dosazeno už nezávisle na cílovém jazyce.

Spojování přeložených řetězců a normálních je možné:

T("blah ") + name

ale obrácené není:

name + T(" blah")   # invalid!

Lepší je proto takovýto kód:

T("blah %(name)s blah", dict(name='Tim'))

nebo alternativní syntaxe:

T("blah %(name)s blah") % dict(name='Tim')

V obou případech překlad proběhne před dosazením jména. Ale takováto varianta by se používat NEMĚLA:

T("blah %(name)s blah" % dict(name='Tim'))

protožee překlad se volá až po dosazení.

Cílový jazyk je určen pomocí "Accept-Language" údaje z HTTP hlavičky, ale tato volba může být přepsána programově vyžádáním nějakého jazyka, například:

T.force('it-it')

což načte "languages/it-it.py" jazykový soubor. Jazykové soubory lze vytvářet z administračního rozhraní.

Můžete také jazyk vynutit jen pro jednotlivý překlad:

T("Hello World", language="it-it")

Můžete zcela potlačit překlad:

T.force(None)

Překlad je prováděn jako opožděný (lazily) během formátování ve view. Takže force metodu lze zavolat kdykoli dříve, než předáme řízení do pohledu.

Opožděný (lazy) překlad lze zakázat:

T.lazy = False

V takovém případě je překlad proveden přímo T operátorem.

Lze také opožděné vyhodnocení zakázat pro jednotlivé řetězce:

T("Hello World", lazy=False)

Běžná nepříjemnost je následující: Originální aplikace je např. anglicky. Předpokládejme, že je přidán jazykový soubor např. pro italštinu, "it-it.py", a HTTP klient ohlašuje, že akceptuje obojí, English (en) i Italian (it-it), v uvedeném pořadí. Web2py neví, že aplikace je napsána v angličtině, najde jen jazykový soubor pro italštinu a přeloží stránky do italštiny. Kdyby italský jazykový soubor nenašlo, vrátilo by správně výstup v angličtině.

Jsou 2 řešení tohoto problému. Buď pro angličtinu vytvořit nadbytečný a nepotřebný jazykový soubor také, nebo lépe, sdělit Web2py, které jazyky mají použít originální text z aplikace. To lze udělat takto:

T.set_current_languages('en', 'en-en')

Tím je do T.current_languages zapsán seznam (list) jazyků, které překlad nevyžadují.

Poznamenejme, že "it" a "it-it" jsou odlišné jazyky z pohledu Web2py. Aby byly podporovány oba, musíme nakopírovat druhý překladový soubor - oba jsou pojmenovány pomocí lowercase (malých písmen). Totéž může platit pro jiné jazyky.

Právě generovaný jazyk je v atributu

T.accepted_language

Pamatujme, že T(...) může překládat řetězce, ale také obsah proměnných:

>>> a="test"
>>> print T(a)

V tomto případě bude slovo "test" přeloženo, ale jestliže nalezeno nebude a lze zapisovat do souborového systému, bude přidáno mezi slova v jazykovém souboru, která vyžadují pozdější překlad.

Cookies

cookies

Web2py používá Python cookies moduly pro správu cookies.

Cookies od prohlížeče jsou v request.cookies a cookies, odesílané serverem zpět prohlížeči, jsou v response.cookies.

Cookie můžete nastavit takto:

response.cookies['mycookie'] = 'somevalue'
response.cookies['mycookie']['expires'] = 24 * 3600
response.cookies['mycookie']['path'] = '/'

Druhý řádek říká prohlížeči, aby cookie udržoval 24 hodin. Třetí řádek říká, aby prohlížeč odesílal cookie kterékoli aplikaci (URL cestě) na aktuální doméně. Poznamenejme, že když byste nezadali cestu pro cookie, prohlížeč bude předpokládat cestu nebo URL, které bylo právě použito, a pošle cookie jen když se URL bude přesně opakovat.

Cookie lze zabezpečit takto:

response.cookies['mycookie']['secure'] = True

Tím řeknete prohlížeči, aby cookie posílal zpět jedině přes HTTPS ane přes HTTP.

Cookie získáme zpět takto:

if request.cookies.has_key('mycookie'):
    value = request.cookies['mycookie'].value

Jestliže sessiony nejsou zakázány, Web2py na pozadí nastaví následující cookie a použije ho pro správu sessiony:

response.cookies[response.session_id_name] = response.session_id
response.cookies[response.session_id_name]['path'] = "/"

Jestliže jedna aplikace zahrnuje více subdomén a vy chcete sdílet sessiony mezi těmito subdoménami (např., sub1.yourdomain.com, sub2.yourdomain.com, apod.), musíte explicitně nastavit doménu pro session cookie takto:

if not request.env.remote_addr in ['127.0.0.1', 'localhost']:
    response.cookies[response.session_id_name]['domain'] = ".yourdomain.com"

Předchozí může být potřebné třeba když chcete uživateli umožnit, aby zůstal přihlášen při přechodu mezi subdoménami.

Aplikace init

init

Když umísťujete Web2py do produkčního prostředí (deploy), budete chtít nastavit výchozí aplikaci, tj. tu, která se spustí, když chybí cesta v URL, například:

http://127.0.0.1:8000

Defaultně Web2py, jakmile narazí na neuvedenou cestu, bude hledat aplikaci s názvem init. Není-li taková aplikace, pokusí se spustit aplikaci welcome.

default_application

Jméno default applikace může být změněno z init na jiné jméno nastavením default_application v routes.py:

default_application = "myapp"

Poznámka: default_application je podporováno od verze Web2py 1.83.

Tady jsou 4 způsoby, jak nastavit defaultní aplikaci:

  • Nazvat svou defaultní aplikaci "init".
  • Nastavit default_application v routes.py
  • Vytvořit odkaz /symbolic link/ z "applications/init" na vstupní adresu vaší aplikace
  • Použít URL rewrite, jak je dále popsáno

URL rewrite (přepis URL)

url rewrite
routes_in
routes_out

Web2py umožňuje přepsat URL cestu příchozího požadavku před určením a zavoláním akce kontroléru (URL mapování), a naopak, Web2py může přepsat výslednou URL cestu, sestavenou pomocí URL funkce (reverzní URL mapování). Jedním důvodem může být /handling legacy URLs/, jiný zjednodušení a zkrácení cest v URL.

Web2py obsahuje 2 různé systémy pro přepis URL: snadný parameter-based systém pro většinu případů, a flexibilnější pattern-based systém pro složitější případy. Pro zadání pravidel přepisu URL, vytvořte ve "web2py" adresáři soubor routes.py, jehož obsah bude záviset na tom, který z uvedených 2 systémů přepisu si vyberete. To je popsáno v následujících 2 sekcích. Oba systémy nelze kombinovat.

Poznamenejme, že když routes.py vyeditujete, musíte ho znovu načíst (reload). Lze to udělat dvěma způsoby: restartem serveru nebo kliknutím na tlačítko "Znovu nahrát cesty" v administračním rozhraní. Pozor: jestliže v routes.py je chyba, převody cest se neaktualizují. Pozn.překladatele: "Znovu nahrát cesty" musíte použít i když vytvoříte nový kontrolér mimo webové prostředí Web2py (zkopírováním a přejmenováním, v jiném editoru, apod.). Poznáte to podle toho, že dostanete chybové hlášení o neexistující akci, v němž je zjevně špatně (kvůli nesprávně inicializovaným cestám) parsovaná URL.

Parameter-based systém

Parameter-based (parametrický) router poskytuje snadný přístup k některým běžným URL-rewrite metodám. Jeho možnosti zahrnují:

* Vypuštění jména default aplikace, kontroléru a funkce z navenek viditelných URL (vytvořených URL funkcí)

* Mapování domén (a/nebo portů) na aplikace nebo kontroléry

* Vložení selektoru aktuálního jazyka do URL

* Odstranění fixního prefixu z příchozí URL a jeho opětovné vložení do výstupní URL

* Mapování kořenových souborů jako např. /robots.txt do static adresáře aplikace

Parametrický router také poskytuje poněkud flexibilnější validaci příchozích URL.

Předpokládejme, že jste napsali aplikaci, nazvanou myapp a chcete ji udělat defaultní tak, aby se jméno aplikace nadále nevyskytovalo v URL viditelném pro uživatele. Váš default kontrolér je obvyklý default a rovněž chcete, aby toto jméno nebylo uživateli viditelné. Zde je popsáno, co je potřeba zadat do routes.py:

routers = dict(
  BASE  = dict(default_application='myapp'),
)

A to je vše. Parametrický router je dostatečně inteligentní, aby věděl, jak co nejlépe zacházet s adresami jako jsou:

http://domain.com/myapp/default/myapp

nebo

http://domain.com/myapp/myapp/index

kde by normální zkrácení mohlo vést k nejednoznačnostem. Máte-li 2 aplikace, myapp a myapp2, dostanete stejný efekt, a navíc default kontrolér z myapp2 bude odstraněn z URL kdekoli je to bezpečné (což je ve většině případů).

Zde je jiný příklad: dejme tomu, že chcete podporovat různé jazyky, zadané pomocí URL, kde vaše URL adresy budou vypadat takto:

http://myapp/en/some/path

nebo (po přepisu)

http://en/some/path

Zde vidíte, jak toho dosáhnout:

routers = dict(
  BASE  = dict(default_application='myapp'),
  myapp = dict(languages=['en', 'it', 'jp'], default_language='en'),
)

Nyní příchozí URL:

http:/domain.com/it/some/path

bude směrována na /myapp/some/path, zatímco request.uri_language bude 'it', takže můžete vynutit /force/ odpovídající překlad. Můžete také mít jazykově-specifické static soubory.

http://domain.com/it/static/filename

bude mapováno na:

applications/myapp/static/it/filename

pokud by takový soubor existoval. Když ale existovat nebude, pak URL jako:

http://domain.com/it/static/base.css

bude mapováno univerzálně:

applications/myapp/static/base.css

(protože nemáme speciální italský static/it/base.css).

Takže nyní můžete mít jazykově specifické static soubory, včetně třeba obrázků, jestliže to potřebujete. Mapování domén je rovněž podporováno:

routers = dict(
  BASE  = dict(
      domains = {
          'domain1.com' : 'app1',
          'domain2.com' : 'app2',
      }
  ),
)

Kód bude dělat to, co je zřejmé.

routers = dict(
  BASE  = dict(
      domains = {
          'domain.com:80'  : 'app/insecure',
          'domain.com:443' : 'app/secure',
      }
  ),
)

mapuje http://domain.com přístupy na kontrolér, pojmenovaný insecure, zatímco HTTPS přístupy na kontrolér secure. Podobně můžete mapovat různé porty na různé aplikace.

Pro další informace prosím nahlédněte do vzorového souboru router.example.py, který se nachází v kořenovém adresáři standardní Web2py distribuce.

Poznámka: Parametrický /parameter-based/ systém byl doplněn do Web2py verze 1.92.1.

Pattern-based systém

Ačkoli právě popsaný parametrický systém by měl dostačovat pro většinu případů, alternativní pattern-based systém poskytuje další flexibilitu pro složitější případy. Chcete-li použít pattern-based systém, místo definice směrovačů jako slovníky (dictionaries) směrovacích parametrů, definujte 2 seznamy (lists) (nebo tuples) dvojčlenných vektorů (tuples) - routes_in a routes_out. Každý vektor obsahuje 2 prvky: vzor /pattern/, který má být nahrazen, a řetězec, který jej bude nahrazovat. Například:

routes_in = (
  ('/testme', '/examples/default/index'),
)
routes_out = (
  ('/examples/default/index', '/testme'),
)

S těmito směrovači URL:

http://127.0.0.1:8000/testme

je mapováno na:

http://127.0.0.1:8000/examples/default/index

Pro návštěvníka všechny odkazy vypadají jako /testme.

Vzory mají syntaxi regulárních výrazů v Pythonu. Například:

  ('.*.php', '/init/default/index'),

mapuje všechny URL, končící na ".php" na index stránku.

Druhá část pravidla může také být přesměrování na jinou stránku:

  ('.*.php', '303->http://example.com/newpage'),

Zde 303 je HTTP kód pro přesměrovanou odpověď.

Potřebujete-li vynechat jméno aplikace, protože zamýšlíte provozovat jen jedinou aplikaci, můžete to udělat takto:

routes_in = (
  ('/(?P<any>.*)', '/init/\g<any>'),
)
routes_out = (
  ('/init/(?P<any>.*)', '/\g<any>'),
)

Je k dispozici také alternativní syntaxe, kterou je možné kombinovat s právě popsaným zápisem pomocí regulárních výrazů. Používá $name místo (?P<name>\w+) nebo \g<name>. Například:

routes_in = (
  ('/$c/$f', '/init/$c/$f'),
)

routes_out = (
  ('/init/$c/$f', '/$c/$f'),
)

tak rovněž vynecháte "/init" aplikační prefix ze všech adres.

Pomocí $name notace můžete mapovat routes_in na routes_out hromadně - předpokládá se tedy, že jste nikde nepoužili regulární výrazy:

routes_in = (
  ('/$c/$f', '/init/$c/$f'),
)

routes_out = [(x, y) for (y, x) in routes_in]

Jestliže výrazu vyhovuje více cest, provede se změna, pro niž URL vyhovuje jako první. Když nevyhovuje žádný vzor, adresa zůstane beze změny.

Můžete zapsat také $anything k označení čehokoli (.*) až do konce řádky.

Tady je minimální "routes.py" pro zpracování požadavků na favicon a robots:

favicon
robots

routes_in = (
  ('/favicon.ico', '/examples/static/favicon.ico'),
  ('/robots.txt', '/examples/static/robots.txt'),
)
routes_out = ()

Zde je složitější příklad, kde zveřejňujeme jedinou aplikaci "myapp" bez prefixování jménem aplikace, a současně zveřejňnujeme admin, appadmin a static:

routes_in = (
  ('/admin/$anything', '/admin/$anything'),
  ('/static/$anything', '/myapp/static/$anything'),
  ('/appadmin/$anything', '/myapp/appadmin/$anything'),
  ('/favicon.ico', '/myapp/static/favicon.ico'),
  ('/robots.txt', '/myapp/static/robots.txt'),
)
routes_out = [(x, y) for (y, x) in routes_in[:-2]]

Úplná syntaxe pro cesty (routes) je složitější než jsme zatím viděli na jednoduchých příkladech. Tady je obecnější a více možností ukazující příklad:

routes_in = (
 ('140.191.\d+.\d+:https?://www.web2py.com:post /(?P<any>.*).php',
  '/test/default/index?vars=\g<any>'),
)

Mapuje http nebo https POST požadavky (všimněte si "post" psaného malými (lowercase) písmeny) na hostitele www.web2py.com ze vzdálené IP adresy, která vyhovuje regulárnímu výrazu

'140.191.\d+.\d+'

a která požaduje stránku, vyhovující regulárnímu výrazu

'/(?P<any>.*).php'

na

'/test/default/index?vars=\g<any>'

kde \g<any> se nahradí z odpovídajícího regulárního výrazu.

Úplná syntaxe je

'[remote address]:[protocol]://[host]:[method] [path]'

Jestliže první sekce předlohy (tedy vše kromě [path]) chybí, Web2py použije default:

'.*?:https?://[^:/]+:[a-z]+'

Celý výraz znamená regulární výraz, takže "." musí být escapovány a jakýkoli podvýraz můžete zachytit pomocí syntaxe regulárních výrazů Pythonu (?P<...>...). Metoda požadavku, typicky GET nebo POST, musí být zapsána malými písmeny. Vyhovující URL ...?/The URL being matched has had any %xx escapes unquoted./

To umožňuje přesměrovat požadavky podle klientovy IP adresy nebo domény, podle typu požadavku, metody požadavku a podle cesty. Také to umožňuje mapovat různé virtuální hostitele na různé aplikace. Jakýkoli odchycený podvýraz můžete použít při tvorbě cílové URL nebo jej případně předat jako GET proměnnou.

Všechny běžné web servery jako jsou Apache nebo lighttpd mají také vlastní možnosti přepisu URL adres. V produkčním prostředí to může být vhodná volba místo použití routes.py. Ať se rozhodnete pro cokoli, to, co striktně doporučujeme, je nikdy nekódovat natvrdo interní URL odkazy ve vaší aplikaci a striktně vždy používat URL() funkci, aby je sestavila. To ponechá vaši aplikaci více přenosnou pro případ, že by se zpracování cest mělo změnit.

Aplikačně-specifický URL přepis
routes_app

Když použijete pattern-based system, aplikace může nastavovat své přesměrování pomocí vlastního, pro ni specifického routes.py souboru, který umístíte do hlavního adresáře aplikace. To povolíte nakonfigurováním routes_app v hlavním (globálním) routes.py, aby z příchozích URL identifikovalo aplikaci, které bude delegován aplikačně specifický přepis URL - použití vlastního routes.py místo globálního routes.py souboru.

Formát routes_app je stejný jako routes_in, s tím rozdílem, že místo toho, čím budeme nahrazovat, jednoduše zapíšeme jméno aplikace. Jestliže výsledkem routes_app pro příchozí URL není žádné platné jméno aplikace nebo sice je, jenže se nenajde aplikačně specifický routes.py soubor v adresáři aplikace, tak se pravidla v globálním routes.py aplikují jako obvykle.

Poznámka: routes_app bylo přidáno do Web2py verze 1.83.

Defaultní aplikace, kontrolér a funkce
default_application
default_controller
default_function

Když používáte pattern-based systém, tak můžete změnit defaultní jméno aplikace, kontroléru a funkce z init, default, respektive index na jiná jména. V routes.py nastavíte požadovanou hodnotu:

default_application = "myapp"
default_controller = "admin"
default_function = "start"

Poznámka: Tyto položky byly přidané do Web2py verze 1.83.

Směrování při chybě

routes_onerror

routes.py můžete také využít pro přesměrování požadavku v případě chyby na serveru na některou speciální akci. Můžete toto mapování zadat globálně, pro jednotlivé aplikace, pro jednotlivé chybové kódy, nebo pro kombinaci aplikace a chybového kódu. Tady je příklad:

routes_onerror = [
  ('init/400', '/init/default/login'),
  ('init/*', '/init/static/fail.html'),
  ('*/404', '/init/static/cantfind.html'),
  ('*/*', '/init/error/index')
]

Pro každý zadaný vektor je první řetězec porovnán s "[app name]/[error code]". V případě shody bude požadavek přesměrován na URL, uvedené ve druhém prvku vektoru. Jestliže URL ošetření chyby nebude statický soubor, ale akce kontroléru, obdrží tato akce následující GET proměnné:

  • code: HTTP status kód (např., 404, 500)
  • ticket: ve formě "[app name]/[ticket number]" (případně "None", pokud ticket nebyl vystaven)
  • requested_uri: jako ekvivalent request.env.request_uri
  • request_url: jako ekvivalent request.url

Tyto proměnné tedy budou pro akci, ošetřující chybu, k dispozici jako request.vars a podle nich může být sestavena potřebná odezva. Je rozumné, aby akce ošetření chyby vracela originální chybový kód místo defaultního 200 (OK) stavového kódu. Dosáhnete toho nastavením response.status = request.vars.code. Je také možné, aby akce ošetření chyby zasílala mail administrátorovi, včetně odkazu na ticket v admin aplikaci.

Chyby, pro které nebyla v definici v routes.py nalezena žádná shoda, zobrazí defaultní chybovou stránku. I tu můžete konfigurovat zde (prohlédněte si router.example.py a routes.example.py v kořenovém adresáři Web2py):

error_message = '<html><body><h1>%s</h1></body></html>'
error_message_ticket = '''<html><body><h1>Internal error</h1>
     Ticket issued: <a href="/admin/default/ticket/%(ticket)s"
     target="_blank">%(ticket)s</a></body></html>'''

První proměnná obsahuje chybové hláššení pro případ, že je požadována neexistující aplikace nebo akce. Druhá proměnná obsahuje chybové hlášení pro případ, kdy byl vystaven chybový ticket.

routes_onerror pracuje s oběma mechanismy přesměrování.

Spouštění úloh na pozadí

Ve Web2py je každý http požadavek obsloužen v jeho vlastním vlákně. Vlákna se recyklují pro efektivitu a jsou spravována webovým serverem. S ohledem na bezpečnost webový server nastavuje time-out pro každý požadavek. Znamená to, že akce nemají spouštět úlohy, které běží příliš dlouho, nemají vytvářet nová vlákna, ani větvit procesy (je to sice možné, ale nedoporučuje se to).

Správný postup pro úlohy náročné na čas je jejich vykonání na pozadí. Pro to je k dispozici více různých nástrojů - zde popíšeme 3 mechanismy, které jsou vestavěny do Web2py: cron, homemade task queues (proprietární řešení fronty úloh), a scheduler (plánovač).

Pojmem cron nyní budeme rozumět Web2py funkcionalitu, nikoli Cron mechanismus na Unixu. Web2py cron pracuje nezávisle na operačním systému, tedy i na Windows.

Web2py cron je vhodný způsob spouštění, jestliže potřebujete spouštět úlohy na pozadí v plánovaných časech a tyto úlohy zabírají relativně krátkou dobu v porovnání s intervalem mezi dvěma voláními. Každá úloha běží ve vlastním procesu, více úloh může běžet paralelně, a nemáte kontrolu nad tím, kolik úloh poběží. Jestliže výjimečně některá úloha překryje sama sebe, může docházet k uzamčení databáze a extrémní spotřebě paměti.

Web2py scheduler (plánovač) používá jinou strategii. Počet běžících procesů je fixní a procesy mohou běžet na různých strojích. Každý proces se nazývá "worker". Každý worker si přivlastní čekající úlohu když má volno, takže je spuštěna co nejdříve po čase, na nějž byla naplánována ke spuštění. Úloha tedy není spuštěna přesně v naplánovanou dobu, ale může být vykonána s nějakým zpožděním. Nemůže vzniknout více procesů než kolik je workerů a proto nemůže dojít k nečekané konzumaci paměti. Plánované úlohy mohou být definovány v modelech a jsou uloženy v databázi. Web2py scheduler neimplementuje distribuovanou frontu, protože se předpokládá, že čas pro distribuci by mohl být srovnatelný s časem běhu úloh.Worker si bere čekající úlohu z databáze.

Homemade tasks queues (fronty úloh) mohou být v některých případech jednodušší alternativou k použití Web2py scheduleru.

Cron

cron

Web2py cron poskytuje aplikacím možnost vykonávat úlohy v předem nastavených časech, způsobem nezávislým na platformě.

V každé aplikaci je cron funkcionalita definována pomocí crontab souboru:

app/cron/crontab

Syntaxe se drží definice v ref. [cron] (s některými rozšířeními, která jsou specifická pro Web2py).

Znamená to, že každá aplikace může mít svou vlastní cron konfiguraci a že cron konfigurace může být měněna zevnitř Web2py bez ovlivnění hostitelského systému.

Tady je příklad cron tabulky:

0-59/1  *  *  *  *  root python /path/to/python/script.py
30      3  *  *  *  root *applications/admin/cron/db_vacuum.py
*/30    *  *  *  *  root **applications/admin/cron/something.py
@reboot root    *mycontroller/myfunction
@hourly root    *applications/admin/cron/expire_sessions.py

Poslední 2 řádky v tomto příkladu využívají rozšíření proti obvyklé cron syntaxi, které dává k dispozici Web2py navíc.

Soubor "applications/admin/cron/expire_sessions.py" je skutečně dodáván s Web2py, a sice s admin aplikací. Vyhledává expirované sessiony a ruší odpovídající soubory. "applications/admin/cron/crontab" spouští tuto úlohu každou hodinu.

Jestliže úloze/skriptu předchází hvězdička (*) a úloha končí příponou .py, vykoná se ve Web2py prostředí. To znamená, že všechny kontroléry a modely máte k dispozici. Použijete-li 2 hvězdičky (**), MODELy se nevykonajíwill not be executed. To je doporučený způsob volání, protože představuje menší zátěž a obchází možné problémy se zamykáním.

Pamatujte, že skripty/funkce vykonávané ve Web2py prostředí potřebují manuální db.commit(), aby se jejich změny zapsaly do databáze - v opačném případě by jejich změny byly revertovány.

Web2py v shell módu, v němž cron běží, negeneruje chybové tickety ani nesestavuje kontext volání (traceback), proto si nejprve buďte jisti, že kód běží bez chyb, než jej zařadíte jako cronn úlohu. Pravděpodobně nebudete totiž moci vidět případné chyby, když úloha běží z cronu. Dále věnujte péči tomu, jak používáte modely: zámky v databázi, způsobené cron úlohami, mohou blokovat přístup k databázi pro webový server /while the execution happens in a separate process, database locks have to be taken into account in order to avoid pages waiting for cron tasks that may be blocking the database/. Používejte ** syntaxi, jestliže nepotřebujete ve vaší úloze používat databázi.

Můžete také zavolat funkci kontroléru a v tom případě není třeba zadávat cestu. Kontrolér a funkce se spustí pro aplikaci, odkud je úloha vyvolána. Věnujte stejnou pozornost nebezpečím, zmíněným už výše. Příklad:

*/30  *  *  *  *  root *mycontroller/myfunction

Když zadáte @reboot do prvního pole crontab souboru, zadaná úloha se vykoná jen jednou, hned po spuštění Web2py. Můžete tuto vlastnost využít, když chcete před-cachovat, zkontrolovat nebo inicializovat data pro aplikaci. Dejte ale pozor, že cron úlohy jsou vykonávány paralelně s během webových aplikací. Jestliže aplikace není způsobilá obsluhovat požadavky, dokud cron úloha, spouštěná po startu serveru, nedoběhne, měli byste implementovat ošetření této situace. Příklad úlohy, spuštěné jen při startu serveru:

@reboot  *  *  *  *  root *mycontroller/myfunction

Podle okolností spuštění Web2py existují 4 módy práce Web2py cronu.

  • soft cron: dostupný ve všech konfiguracích
  • hard cron: dostupný při použití vestavěného (built-in) webového serveru (ať už přímo nebo pomocí Apache mod_proxy)
  • externí cron: dostupný, máte-li přístup k systémové cron službě
  • žádný cron

Hard cron je defaultní, jestliže používáte vestavěný webový server; ve všech ostatních případech je defaultní soft cron. Soft cron je defaultní metoda, když používáte CGI, FASTCGI nebo WSGI (ale poznamenejme, že soft cron není povolen defaultně ve standardním wsgihandler.py souboru, dodávaném s Web2py).

Při soft cronu bude úloha vykonána při prvním volání (načtení stránky) pomocí Web2py, k němuž dojde po čase, zadaném v crontab; vykonání úlohy následuje až po načtení stránky, takže uživatel nezaznamená žádné zpoždění. Samozřejmě že je tu určitá nejistota ohledně toho, kdy skutečně bude úloha vykonána, která souvisí s intenzitou provozu, který web má. Také může dojít k přerušení (nedokončení) úlohy, jestliže web server má nastaven timeout pro načtení stránky (page load). Jestliže tato omezení nejsou akceptovatelná, musíte se snažit použít externí cron. Soft cron může fungovat, ale jestliže váš web server nabízí jiné cron módy, měli byste je určitě využít místo soft cronu.

Hard cron je defaultní, když používáte vestavěný web server (přímo nebo přes Apache mod_proxy). Hard cron se vykonává v paralelním vlákně, takže odpadají problémy soft cronu s možným omezením doby běhu a s přesností spuštění.

Externí cron není nastaven jako default v žádné konfiguraci a vyžaduje, abyste k systémové cron službě měli přístup. Běží v samostatném procesu, takže žádné z omezení soft cronu se ho netýká. Je to doporučený způsob použití cronu pod WSGI nebo FASTCGI.

Příklad zápisu řádky do systémové crontab, (obvykle /etc/crontab):

0-59/1 * * * * web2py cd /var/www/web2py/ && python web2py.py -J -C -D 1 >> /tmp/cron.output 2>&1

Využíváte-li externí cron, přidejte -N parametr příkazové řádky při spuštění Web2py pro obsluhu stránek nebo jinak konfigurujte Web2py, aby nedošlo ke kolizi mezi různými módy cronu. Také v případě použití externího cronu a spouštění Web2py úlohy, přidejte do volání Web2py parametr -J (nebo --cronjob, což je totéž), aby Web2py poznalo, že spouští úlohu, vyvolanou cronem. V případu hard nebo soft cronu si toto Web2py ošetří automaticky.

V případech, kdy některý Web2py proces nemá mít žádnou cron funkcionalitu, použijte -N parametr příkazové řádky pro zákaz cronu. Tím vypnete případné údržbové úlohy (jako je automatické mazání souborů v session adresářích). Nejčastější důvody pro použití tohoto parametru jsou když:

  • už jste konfigurovali externí cron, řízený systémem (obvyklé řešení při WSGI instalaci);
  • chcete ladit vaší aplikaci s jistotou, že cron nebude interferovat ani s vystavováním stránek.

Homemade task queues (proprietární řešení fronty úloh)

Ačkoli cron je užitečný ke spouštění úloh v pravidelných časových intervalech, ne vždy je to nejlepší řešení ke spouštění úloh na pozadí. Z toho důvodu má Web2py možnost spustit jakýkoli Python skript jako by byl volán uvnitř kontroléru:

python web2py.py -S app -M -N -R applications/app/private/myscript.py -A a b c

kde -S app říká Web2py, aby vykonalo "myscript.py" jako "app", -M říká Web2py, aby vykonalo předem modely, -N zakazuje tomuto procesu spustit cron, a -A a b c předává command line parametry sys.args=['a','b','c'] pro skript "myscript.py".

Takovýto typ procesů na pozadí by neměl být vykonáván cronem (snad s výjimkou cron @reboot), protože si potřebujete být jisti, že nepoběží více než jedna instance v určitém čase. V případě cronu se může stát, že nedokončené a opakovaně spouštěné úlohy zahltí server.

/Pozn.překladatele: autor má zřejmě na mysli, že tuto frontu bude zpracovávat jeden pro to určený, trvale běžící proces./

V kapitole 8 si ukážeme příklad, jak právě naznačenou metodou rozesílat emaily.

Plánovač /Scheduler/ (experimentální)

Web2py scheduler pracuje velmi podobně frontě úloh, o níž jsme právě mluvili, s některými rozdíly:

  • Poskytuje standardní mechanismus pro vytváření a plánování úloh.
  • Fronta není obsluhována jedním procesem na pozadí, ale sadou procesů - workerů.
  • Práce workerů může být kontrolována, protože jejich stav, podobně jako stav zpracovávaných úloh, se zaznamenávají do databáze.
  • Pracuje i nezávisle na Web2py, to zde ale není dokumentováno.

Scheduler nepoužívá cron, ačkoli můžete použít cron @reboot k rozběhnutí workerů.

V scheduleru je každá úloha funkcí, definovanou v modelu (nebo v modulu, který model importuje). Například:

def task_add(a,b):
    return a+b

Úlohy se vždy vykonávají ve stejném prostředí - takovém, jaké vidí kontroléry, takže vždy jsou dostupné globální proměnné z modelu, zejména databázové spojení (db). Úlohy se liší od kontrolérových akcí, protože nejsou asociovány s HTTP požadavkem, a proto tady chybí i request.env.

Abyste povolili scheduler, je třeba v modelu jeho instanci. Doporučený způsob, jak zprovoznit scheduler v aplikaci je vytvoření modelu, pojmenovaného scheduler.py a definování funkcí v něm. Za definici funkcí je pak vhodné přidat následující kód:

from gluon.scheduler import Scheduler
myscheduler = Scheduler(db)

Pozn.: Jsou-li vaše úlohy importovány zz modulu, budete pravděpodobně muset restartovat workery.

Parametry

První argument Scheduler třídy je databáze, kterou má scheduler použít pro sledování komunikace s workery. Může to být buď db příslušné aplikace, nebo jiná, nejspíše pro tento účel vyhrazená databáze, případně i sdílená k tomuto účelu více aplikacemi. Používáte-li SQLite, doporučuje se k tomuto účelu použít oddělenou databázi (jinou než databáze aplikace), aby nebyly ohroženy provozní odezvy aplikace. Jakmile jsou úlohy definovány a vytvořena instance Scheduleru, je ještě třeba nastartovat worker procesy. To můžete udělat několika způsoby:

python web2py.py -K myapp

spustí worker pro aplikaci myapp. Chcete-li nastartovat více workerů pro stejnou aplikaci, můžete to udělat tak, že zadáte: myapp,myapp.

Pozn.překladatele: Více workerů můžete také nastartovat dalšími příkazy

python web2py.py -K myapp

Můžete také zadat skupinová jména ?/overriding the one set in your model/ takto /pozn.překladatele: nejsem si jist, zda jde o přepis jména nebo o volbu ze jmen, zadaných parametrem group_names při vytvoření instance Scheduler() v modelu (defaultní jméno skupiny je: main)/

python web2py.py -K myapp:group1:group2,myotherapp:group1

Pokud jste definici modelu pro scheduler pojmenovali scheduler.py, můžete spustit i zastavit workery z okna Web2py (z toho, kde při spuštění zadáváte ip adresu, port a heslo).

Pokud pracujete s vestavěným serverem, můžete i spustit web server současně s schedulerem jediným příkazem (nechcete-li, aby se zobrazilo Web2py okno nebo ..?/or you can use the "Schedulers" menu instead/).

python web2py.py -a "heslo" -K myapp -X

Můžete zadat obvyklé parametry (-i, -p, -a (v příkladu zadáním -a potlačujete zobrazení úvodního okna)), předat seznam aplikací do -K parametru a přidat -X. Scheduler pak obsluhuje všechny zadané aplikace.

Úplné parametry scheduleru jsou:

Scheduler(
    db,
    tasks=None,
    migrate=True,
    worker_name=None,
    group_names=None,
    heartbeat=HEARTBEAT,
    max_empty_runs=0,
    discard_results=False,
    utc_time=False
)

Seznamme se s nimi postupně:

  • db je DAL instance - připojení k databázi, kde si scheduler vytvoří své tabulky.
  • tasks může být slovník (dict). Je potřeba ho definovat, pokud funkce nechcete volat přímo jejich jmény, např. tasks=dict(mynameddemo1=demo1) vám umožní vykonávat funkci demo1 pomocí st.insert(task_name='mytask', function_name='mynameddemo1') nebo st.insert(task_name='mytask', function_name='demo1'). /Pozn. překladatele: st znamená tabulku db.scheduler_task - přidáním záznamu do ní plánujete úlohy, jak je popsáno níže/
  • worker_name je defaultně None. Jakmile se worker spustí, jeho jméno se vygeneruje jako hostname-uuid. Chcete-li jméno určit, dejte pozor, aby bylo unikátní.
  • group_names je defaultně nastaveno na [main]. Všechny úlohy mají parametr group_name (jméno skupiny), nastavený defaultně na main. Workery mohou brát úlohy jen z jim přiřazených skupin.

Pozn.: To je užitečné, když máte více worker procesů na různých strojích a chcete úlohu přidělit některému konkrétnímu workeru. Pozn.: Workeru může být přiřazeno více skupin, mohou i být stejné, např. ['mygroup','mygroup']. Úlohy se rozdělují s uvážením, že worker s group_names ['mygroup','mygroup'] je schopen zpracovat více úloh než worker s group_names ['mygroup'].

  • heartbeat je defaultně nastaveno na 3 vteřiny. Tento parametr říká, jak často bude scheduler kontrolovat svůj status v tabulce scheduler_worker, aby se podíval, zda mu byla přidělena nějaká úloha, kterou má vykonat.
  • max_empty_runs je defaultně 0, a to znamená, že worker bude trtvale pracovat a zpracovávat úlohy, jakmile mu budou přiděleny. Když tuto hodnotu nastavíte na např. 10, worker se automaticky ukončí, jestliže 10x (v intervalu 3 vteřiny, resp. heartbeat) zjistil, že nemá přidělenou žádnou práci.
  • discard_results je defaultně False. Jestliže jej nastavíte na True, nevytvářejí se záznamy v databázi v tabulce scheduler_run pro spuštěné úlohy.

Pozn.: záznamy v tabulce scheduler_run se ale i tak budou vytvářet pro FAILED, TIMEOUT a STOPPED statusy úloh.

  • utc_time je defaultně False. Potřebujete-li spolupracovat s workery v různých časových zónách, /...or don't have problems with solar/DST times, supplying datetimes from different countries, etc, you can set this to True. The scheduler will honor the UTC time and work leaving the local time aside.../ Po změně na True ale musíte plánovat úlohy (start_time, stop_time, a podobně) v UTC čase.

Nyní tedy máme potřebnou infrastukturu: definované úlohy, scheduler je o nich informován, a jsou spuštěné workery. Zbývá plánovat vykonání úloh.

Úlohy

Vykonání úloh lze plánovat programově nebo pomocí appadmin (kde vidíme tabulky scheduleru).

Pozn.překladatele: Funguje to takto: Do databáze scheduler přidá tabulky scheduler_worker (seznam worker procesů), scheduler_task (kam můžete plánovat úlohy) a scheduler_run (kam se zaznamenávají údaje o jednotlivých bězích úloh). Přidáním záznamu do scheduler_task, kde do function_name uvedete jméno funkce v scheduler.py modelu, připravíte úlohu do fronty. Při každém spuštění úlohy se založí záznam v tabulce scheduler_run. Po skončení úlohy, jestliže úloha vrátí None, záznam bude odstraněn. V opačném případě zůstane a vrácená hodnota bude zobrazena v poli results. Pomocí příkazu print může také úloha zapisovat do pole output.

Úloha naplánujete jednoduše přidáním položky do tabulky "scheduler_task", appadmin ji zobrazuje na této adrese:

http://127.0.0.1:8000/myapp/appadmin/insert/db/scheduler_task

Význam položek tabulky je zřejmý. "args" a "vars"" pole jsou hodnoty, které budou úloze předány, v JSON formátu. Např. pro "task_add" úlohu, definovanou výše v textu, by příklad "args" a "vars" mohl být:

args = [3, 4]
vars = {}

nebo

args = []
vars = {'a':3, 'b':4}

Všechny úlohy procházejí životním cyklem:

scheduler tasks

Pojďme po pořádku. Normálně, jakmile pošlete úlohu do scheduleru, budete chtít, aby byla vykonávaná. Bude mít QUEUED status (zařazena do fronty). Chcete-li první spuštění odložit, použijte parametr start_time (default = now). Jestliže z nějakých důvodů si potřebujete být jisti, že úloha bude vykonávána jen do určité doby, můžete nastavit stop_time (default = None). Jestliže úloha nebude přijata workerem před stop_time, bude její stav nastaven na EXPIRED. Úlohy bez uvedeného stop_time set nebo na něž dojde řada před uplynutím stop_time jsou přiřazovány workerům. Jakmile worker získá úlohu, je označena jako RUNNING. RUNNING úlohy mohou skončit se statusem:

  • TIMEOUT, jestliže úloha nedoběhla za dobu, zadanou parametrem timeout parameter (default = 60 vteřin)
  • FAILED došlo-li k neošetřené výjimce
  • COMPLETED když vše proběhlo ok

Dále můžete kontrolovat, kolikrát bude úloha zopakována - nastavíte parametr repeats (1 = 1x, 0 = bez omezení počtu). Můžete zadat minimální interval pomocí parametru period (default = 60 vteřin). Pozn.: čas se počítá od spuštění (nikoli od dokončení) do dalšího spuštění. Pozn. překladatele: jedná se o minimální interval - skutečný bude větší, protože nemusí být volný worker a i když je, je zde nějaká režie se spuštěním úlohy.

Dále můžete zadat, kolikrát funkce může vrátit výjimku (např. když chcete data od pomalé webové služby) a bude znovu zařazena do fronty, místo aby byla ukončena jako FAILED - k tomu je určen parametr retry_failed (default = 0, -1 = unlimited).

task repeats

Máte tedy k dispozici

  • period a repeats pro opakované zařazení úlohy do fronty
  • timeout pro omezení času při selhání
  • retry_failed pro řízení, kolikrát se pokusit opakovat po selhání
  • start_time a stop_time pro povolení úlohy jen během nějaké doby
Oznamování průběhu (procent)

Speciální povel v příkazu print smaže dosavadní výstup: povel !clear!. To spolu s nastaveným parametrem sync_output vám umožní zobrazovat postup úlohy v poli output` tabulky scheduler_run. Pracuje to takto:

import time
def reporting_percentages():
    time.sleep(5)
    print '50%'
    time.sleep(5)
    print '!clear!100%'
    return 1

Po 5 sekundách funkce vypíše 50%. To se nejpozději za počet vteřin, uvedený v sync_output objeví v tabulce scheduler_run v poli output. !clear! v příkazu print dosavadní hodnotu pole output smaže a nahradí ji hodnotou 100%.

db.scheduler_task.validate_and_insert(task_name='percentages', function_name='reporting_percentages', sync_output=2)
Results a output

Tabulka "scheduler_run" ukládá stav všech spuštěných úloh. Každý záznam znamená úlohu, kterou převzal worker. Jedna úloha může mít postupně více běhů. Pozor na to, že když úloha nemá return hodnoty (vrátí None), je po dokončení z tabulky scheduler_run odstraněna.

Možné stavy po spuštění jsou:

RUNNING, COMPLETED, FAILED, TIMEOUT

Jestliže úloha doběhla, nevznikla výjimka, a nedošlo k timeoutu, běh (v scheduler_run) se označí jako COMPLETED a úloha (v scheduler_task) jako QUEUED nebo COMPLETED, podle toho, zda má běžet opakovaně nebo ne. Vrácená hodnota z funkce je serializována do JSON a uložena do scheduler_run záznamu.

Jestliže RUNNING úloha generuje výjimku, běh se označí jako FAILED a úloha jako FAILED. Traceback (hierarchie volání havarovaného příkazu) se uloží do scheduler_run záznamu.

Podobně, když doba běhu přesáhne timeout, běh je zastaven a označen TIMEOUT, podobně úloha je označena TIMEOUT.

Ve všech případech stdout (výstup, např. z příkazů print) je zachycen a rovněž loggován do scheduler_run záznamu.

Pomocí appadminu můžete kontrolovat RUNNING úlohy, výstup z COMPLETED úloh, chyby FAILED úloh, apod.

Scheduler vytváří ještě tabulku "scheduler_worker", která obsahuje informace o worker procesech.

Správa worker procesů

?/Worker fine management is hard. This module tries not to leave behind any platform (Mac, Win, Linux)./

Spustíte-li worker proces, budete jej chtít později:

  • likvidovat (kill), bez ohledu na to, co dělá
  • likvidovat (kill), jestliže nevykonává úlohy
  • uspat

Např. chcete šetřit zdroje (resources). Spouštíte např. úlohy každou hodinu takže po vykonání úloh chcete ukončit automaticky workery. To všechno lze udělat nastavením parametrů Scheduleru nebo tabulky scheduler_worker. Přesněji, pro každý spuštěný worker můžete měnit jeho status, a tak ovlivnit jeho chování. Workery mohou mít některý ze stavů: ACTIVE, DISABLED, TERMINATE nebo KILLED.

ACTIVE a DISABLED jsou trvalé stavy, kdežto TERMINATE a KILL, jak jména napovídají, jsou spíše "příkazy" než stavy. Stisknutí ctrl+c je totožné s převedením workeru do stavu KILL.

workers statuses

Vše, co je možné dělat pomocí appadmin, lze dělat také programově, vkládáním a updatem záznamů v scheduler tabulkách.

Měli byste se ale vyvarovat aktualizaci záznamů u RUNNING úloh, protože to může způsobit neočekávatelné chování. Nejlepší postup je řadit úlohy do fronty pomomcí "validate_and_insert". Například:

db.scheduler_task.validate_and_insert(
    function_name='task_add',
    args='[]',
    vars="{'a':3,'b':4}",
    repeats = 10, # run 10 times
    period = 3600, # every 1h
    timeout = 120, # should take less than 120 seconds
    )
  • K

Pole "times_run", "last_run_time" a "assigned_worker_name" se nepoužívají při zadání úlohy, ale zapisuje do nich worker.

Výstupy např. dokončených úloh můžete zpracovat:

completed_runs = db(db.scheduler_run.status='COMPLETED').select()

Scheduler je označen jako experimentální (bez garance zpětné kompatibility), protože potřebuje další testy a protože se struktura tabulek může měnit s přidáváním dalších vlastností.

Moduly třetích stran

import

Web2py je napsáno v Pythonu, takže pochopitelně může importovat a použít kterýkoli modul Pythonu včetně modulů třetích stran. Jen musí požadovaný modul správně nalézt. Jako pro kteroukoli Python aplikaci, moduly mohou být instalovány do příslušného adresáře Pythonu "site-packages" a pak mohou být importovány odkudkoli z vašeho kódu.

Moduly v "site-packages" adresáři jsou, jak jméno adresáře napovídá, site-level, tedy globální pro všechny aplikace. Aplikace, které site-level moduly používají, nejsou přenositelné, pokud odděleně neinstalujeme také potřebné moduly. Výhodou umístění modulů v "site-packages" adresáři je, že je může více aplikací sdílet. Uvažujme např. kreslící package "matplotlib". Můžeme jej instalovat za shellu třeba pomocí příkazu easy_install:

easy_install py-matplotlib

a pak můžete package importovat do kteréhokoli modelu/kontroléru/view:

import matplotlib

Web2py source distribuce (distribuce zdrojovýcch kódů) a Windows binární distribuce mají site-packages adresář uvnitř hlavního adresáře. Mac binární distribuce má site-packages umístěno ve složce:

web2py.app/Contents/Resources/site-packages

Pozn. překladatele: Podobně lze importovat také moduly, instalované do site-packages adresáře Pythonu. Podrobnosti o importu modulů (o proměnné sys.path a __init__.py souborech) hledejte v dokumentaci Pythonu.

Problém s použitím site-packages je ten, že je obtížné použít odlišné verze stejného modulu současně, např. můžete mít 2 aplikace, ale každá by chtěla použít jinou verzi téhož souboru. Změna sys.path nepomůže, protože ovlivní obě aplikace.

Pozn. překladatele: Standardní možností řešení je virtualenv.

Pro řešení tohoto problému nabízí Web2py jiný mechanismus importu modulů, při němž globální sys.path nijak neměníme: umístěním modulu do "modules" složky aplikace. Jedním z vedlejších kladů je, že takový modul se automaticky zkopíruje, zálohuje a distribuuje s aplikací.

Jakmile modul "mymodule.py" umístíte do "modules/" složky aplikace, může být také importován odkudkoli z této web2py aplikace a není třeba měnit sys.path. Použijeme standardní příkaz:

import mymodule

Pozn. překladatele: Aplikace dá tedy přednost modulu zz vlastní složky "modules" a pokud tam požadovaný modul nenajde, hledá jej standardně.

Prostředí pro provádění kódu (Execution environment)

exec_environment

Ačkoli vše zde uváděné platí, doporučujeme vám místo toho vývoj vaší aplikace pomocí komponent, jak je popsáno v kapitole 12.

Web2py soubory modelů a kontrolérů nejsou moduly Pythonu, protože je nelze importovat do Pythonu pomocí příkazu import. Důvodem pro to je, že modely a kontroléry jsou designovány, aby byly vykonány v připraveném prostředí, v němž jsou předpřipraveny Web2py globální objekty (request, response, session, cache a T) a helper funkce. To je potřebné, protože Python je staticky (lexikálně) orientovaný jazyk, kdežto Web2py prostředí je vytvářeno dynamicky. /..is a statically (lexically) scoped language, whereas the web2py environment is created dynamically/.

Web2py poskytuje exec_environment funkci, která vám umožňuje přístup k modelům a kontrolérům přímo. exec_environment vytvoří Web2py prostředí pro provádění kódu, natahne (load) do něj soubor a pak vrátí Storage objekt s prostředím. Storage objekt také slouží jako namespace mechanismus. Každý soubor Web2py, designovaný pro vykonání v prováděcím prostředí, může být načten a vykonán pomocí exec_environment. Možná použití exec_environment jsou:

  • Přístup k datům (modelům) z jiných aplikací.
  • Přístup ke globálním objektům z jiných modelů a kontrolérů.
  • Vykonávání funkcí cizích kontrolérů z jiného kontrolérů.
  • Načtení knihoven helperů společných pro více/všechny aplikace.

Tento příklad čte řádky z user tabulky, definované v modelu cas aplikace:

from gluon.shell import exec_environment
cas = exec_environment('applications/cas/models/db.py')
rows = cas.db().select(cas.db.user.ALL)

Jiný příklad: uvažujme kontrolér "other.py", který obsahuje:

def some_action():
    return dict(remote_addr=request.env.remote_addr)

Takto můžete tuto akci zavolat z jiného kontroléru (nebo z Web2py shellu):

from gluon.shell import exec_environment
other = exec_environment('applications/app/controllers/other.py', request=request)
result = other.some_action()

V řádku 2 request=request je nepovinné. Způsobí předání aktuálního request do prostředí "other". Bez tohoto předání by prostředí "other" obsahovalo nový a prázdný request objekt (s výjimkou request.folder). Podobně lze předat do prováděcího prostředí také response a session objekty. Buďte opatrní, potřebujete-li předat request, response a session objekty --- modifikace ve volané akci ?/..or coding dependencies in the called action/ mohou způsobit neočekávané postranní efekty.

Volání akce v řádku 3 neprovede pohled (view); jen vrátí dictionary, která se normálně předává do pohledu. Chcete-li výsledek pohledu, musíte dodatečně použít response.render.

Jedno varování závěrem. Nepoužívejte exec_environment nesprávným způsobem. Chcete-li výsledky akce z jiné aplikace, pravděpodobně byste spíše měli implementovat XML-RPC API, než akci volat (implementace XML-RPC API pomocí Web2py je téměř triviální). Určitě nepoužívejte exec_environment jako mechanismus přesměrování; k tomu slouží funkce redirect.

Spolupráce

cooperation

Je více způsobů, jak aplikace mohou spolupracovat:

  • Aplikace se mohou připojovat ke stejné dattabázi a sdílet tabulky. Nenní nutné, aby všechny tabulky v databázi byly definovány ve všech aplikacích, ale musí být definovány ty tabulky, které aplikace používá. Všechny aplikace, které používají stejnou tabulku (nejvýše s výjimkou jedné z nich), ji musí definovat s migrate=False.
  • Aplikace mohou integrovat komponenty z ostatních aplikací za pomoci LOAD helperu (popsáno v kapitole 12).
  • Aplikace mohou sdílet sessiony.
  • Aplikace se mohou volat mezi sebou vzdáleně pomocí XML-RPC.
  • Aplikace mohou vzájemně otevírat své soubory ve filesystému (samozřejmě jen pokud stejný souborový systém sdílejí).
  • Aplikace mohou lokálně volat akce jiné aplikace za pomoci exec_environment, jak bylo popsáno výše.
  • Aplikace může importovat moduly jiné aplikace pomocí syntaxe:
from applications.appname.modules import mymodule
  • Aplikace mohou importovat jakékoli moduly ze společné cesty PYTHONPATH, která je načtena do sys.path.

Jedna aplikace může načíst session jiné aplikace za pomoci příkazu:

session.connect(request, response, masterapp='appname', db=db)

"appname" je zde jméno hlavní aplikace, tedy té, která nastaví původní session_id v cookie. db je databázové připojení do databáze, která obsahuje tabulku (web2py_session) pro ukládání session. Všechny aplikace, které mají sdílet sessiony, musí připojovat tuto společnou databázi pro ukládání session.

Jak již bylo řečeno, jedna aplikace může importovat modul z jiné aplikace takto

import applications.otherapp.modules.othermodule

Logování (Logging)

Python poskytuje API pro logování. Web2py poskytuje mechanismus ke konnfiguraci této API, takže aplikace ji mohou jednotným způsobem využívat.

Ve své aplikaci můžete vytvořit logger, například v modelu:

import logging
logger = logging.getLogger("web2py.app.myapp")
logger.setLevel(logging.DEBUG)

a můžete ho použít pro logování hlášení různých úrovní důležitosti.

logger.debug("Můžete nahlédnout.. %s" % details)
logger.info("Snad víte, že %s" % details)
logger.warn("Pamatujte, že %s" % details)
logger.error("Ajaj, stato se špatně toto.. %s" % details)

logging je standardní modul Pythonu, je popsán zde:

http://docs.python.org/library/logging.html

Řetězec "web2py.app.myapp" definuje logger na úrovni aplikace.

Aby to pracovalo správně, potřebujete konfigurační soubor pro logger. Jeden najdete v kořenovém adresáři Web2py: "logging.example.conf". Je třeba ho přejmenovat na "logging.conf" a je-li třeba, tak jej upravit.

Soubor je samo-dokumentující se, takže stačí jej otevřít, a přečíst si, co je potřeba.

Pro vytvoření konfigurovatelného loggeru aplikace "myapp", musíte přidat myapp do seznamu klíčů loggeru:

[loggers]
keys=root,rocket,markdown,web2py,rewrite,app,welcome,myapp

a musíte připsat sekci [logger_myapp], přičemž sekci [logger_welcome] využijte jako vzor.

[logger_myapp]
level=WARNING
qualname=web2py.app.myapp
handlers=consoleHandler
propagate=0

"handlers" direktiva určuje typ logování a zde bude "myapp" logovat na konzoli.

WSGI

WSGI

Web2py a WSGI mají k sobě přátelský i odtažitý /love-hate/ vztah. Naše perspektiva je, že WSGI bylo vyvinuto jako protokol pro připojení web serveru a web aplikace přenosným způsobem, a k tomuto účelu ho používáme. Web2py ve svém jádře je WSGI aplikace: gluon.main.wsgibase. Někteří vývojáři posunuli WSGI na hranici jejích možností jako protokol pro middleware komunikaci a vyvíjejí webové aplikace trochu podobné cibuli s mnoha vrstvami (každá vrstva je WSGI middleware, vyvinutý autonomně na frameworku). Web2py interně nepřijalo takovou konstrukci. Je to proto, že máme dojem, že zásadní funkcionalita frameworku (správa cookies, session, ošetření a reportování chyb, transakce, dispatching) mohou být lépe optimalizovány z hlediska rychlosti a bezpečnosti, jestliže se s nimi zachází v jedné kompaktní vrstvě.

Nicméně Web2py vám umožňuje použít WSGI aplikace a middleware třetích stran třemi způsoby (nebo jejich kombinacemi):

  • Můžete editovat soubor "wsgihandler.py" a zapojit WSGI middleware třetí strany.
  • Můžete zapojit jiný WSGI middleware do kterékoli konkrétní akce kontroléru ve vaší aplikaci.
  • Můžete volat WSGI aplikace třetích stran z vašich akcí.

Omezením je jen to, že nemůžete pomocí jiného WSGI middlewaru nahradit klíčové Web2py funkce.

Externí middleware

Uvažujme soubor "wsgibase.py":

#...
LOGGING = False
#...
if LOGGING:
    application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
                                        logfilename='httpserver.log',
                                        profilerfilename=None)
else:
    application = gluon.main.wsgibase

Nastaví-li se LOGGING na True, gluon.main.wsgibase je zabalena do middleware funkce gluon.main.appfactory. Ta provádí logování do "httpserver.log" souboru. Podobným způsobem můžete zařadit jakýkoli jiný middleware třetí strany. Odkazujeme na oficiální WSGI dokomentaci pro více podrobností.

Interní middleware

Mějme kteroukoli akci ve vašich kontrolérech (například index) a jakoukoli middleware application třetí strany (např. MyMiddleware, který bude konvertovat výstup na upper-case). Můžete použít Web2py dekorátor k zapojení middleware k této akci. Tady je příklad:

class MyMiddleware:
    """konvertuje výstup na upper case"""
    def __init__(self,app):
        self.app = app
    def __call__(self, environ, start_response):
        items = self.app(environ, start_response)
        return [item.upper() for item in items]

@request.wsgi.middleware(MyMiddleware)
def index():
    return 'hello world'

Nemůžeme ale slíbit, že všechny middleware softwary třetích stran půjdou tímto způsobem propojit.

Volání WSGI aplikací

Je snadné z Web2py akce WSGI aplikaci volat. Tady je příklad:

def test_wsgi_app(environ, start_response):
    """toto je pokusná WSGI aplikace"""
    status = '200 OK'
    response_headers = [('Content-type','text/plain'),
                        ('Content-Length','13')]
    start_response(status, response_headers)
    return ['hello world!\n']

def index():
    """pokusná akce, která volá předchozí aplikaci a sestaví /escapes/ výstup"""
    items = test_wsgi_app(request.wsgi.environ,
                          request.wsgi.start_response)
    for item in items:
        response.write(item,escape=False)
    return response.body.getvalue()

V tomto případě index akce volá test_wsgi_app a upraví získanou hodnotu před jejím vrácením. Všimněme si, že index sama o sobě není WSGI aplikací a musí použít normální Web2py API (jako response.write pro zápis socketu).

 top