Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
90/225
Obsah:
V dnešní době je nespočet technologií, které lze použít pro vytváření dynamických webových stránek. Jednou z možností je použít programovací jazyk Python. Vývoj webových aplikací v holém Pythonu by ale byl příliš složitý a pracný. Proto se většinou používají frameworky, které celou věc zjednoduší a umožňují aby se programátor soustředil na to podstatné.
Pro Python existuje web-frameworků celá řada. Za zmínku jistě stojí
Pokud si chcete udělat přehled, jaké možnosti python má určitě si přečtěte článek Znám jen PHP. Jak napíšu webovou aplikaci v Pythonu?.
My se seznámíme s framewokem Bottle. Ten je maximálně jednoduchý a k ničemu nás nenutí. První, malá, nic moc nedělající stránka/aplikace může vypadat například takto:
#!/usr/bin/python
# -*- coding: utf8 -*-
# Soubor: hello.py
# Datum: 07.02.2014 10:26
# Autor: Marek Nožka, nozka <@t> spseol <d.t> cz
# Licence: GNU/GPL
# Úloha: Hello World Bottle
############################################################################
from bottle import route, run
@route('/')
@route('/ahoj')
def hello():
return """<h1>Hurá Hurá!</h1>
<p>Funguje to!</p>"""
run(host='localhost', port=8090, debug=True, reloader=True)
`--> stáhnout
Vytvořený soubor uložíme na disk a spustíme. Spustí se tak malý vývojový
web-server, který naslouchá na portu 8090.
$ python hello.py
Nyní se na náš první výtvor můžeme podívat we webovém prohlížeči na adrese http://localhost:8090/. Pokud v souboru uděláme změny okamžitě se promítnou bez toho aniž bychom aplikaci (web-server) ukončovali a znovu spouštěli.
a=8+'8' a
podívejte se jak na to spuštěná aplikace (web-server) zareaguje.Každé adrese URL lze přiřadit kód, který se má provést.
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
3 # Soubor: name.py
4 # Datum: 07.02.2014 11:12
5 # Autor: Marek Nožka, nozka <@t> spseol <d.t> cz
6 # Licence: GNU/GPL
7 # Úloha: Hello World Bottle +
8 ############################################################################
9
10 from bottle import route, run
11 import datetime
12
13 @route('/')
14 @route('/ahoj')
15 def funguje():
16 return """<h1>Hurá Hurá!</h1>
17 <p>Funguje to!</p>"""
18
19 @route('/cas')
20 def cas():
21 q='<h1>Aktuální čas</h1>'
22
23 td=datetime.datetime.today()
24
25 q+="<p>Aktuální datum: {}. {}. {}</p>".format(td.day, td.month, td.year)
26 q+="<p>Aktuální čas: {}:{}:{}</p>".format(td.hour, td.minute, td.second)
27
28 return q
29
30 @route('/ahoj/<name>')
31 def ahoj(name):
32 return "<h1>Ahoj {0}</h1><p>Funguje mi to! Hurá! Huráááááá.</p>".format(name)
33
34 @route('/nasobeni/<a>/<b>')
35 @route('/<a>*<b>')
36 def nasobeni(a,b):
37 return str(float(a)*float(b))
38
39 @route('/mocnina/<a:float>/<b:int>')
40 @route('/<a:float>^<b:int>')
41 def nocnina(a,b):
42 vysledek=a**b
43 return str(vysledek)
44
45 ##########################################################################
46 run(host='localhost', port=8090, debug=True, reloader=True)
`--> stáhnout
Vyzkoušejte si následující URL a prostudujte zdrojový kód, který je obhospodařuje.
Více o routingu se dozvíte v dokumentaci: http://bottlepy.org/docs/dev/routing.html
Existuje více možností jak přistupovat k datům z formulářů. Záleží na tom, která metoda je použita (GET, POST). Více informací naleznete v dokumentaci:
| Atribut | GET | POST | File Uploads |
|---|---|---|---|
| request.query | yes | no | no |
| request.forms | no | yes | no |
| request.files | no | no | yes |
| request.params | yes | yes | no |
| request.GET | yes | no | no |
| request.POST | no | yes | yes |
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
3 # Soubor: forms.py
4 # Datum: 07.02.2014 11:59
5 # Autor: Marek Nožka, nozka <@t> spseol <d.t> cz
6 # Licence: GNU/GPL
7 # Úloha: Bottle a formuláře
8 ############################################################################
9
10 from bottle import run, route, get, post, request, redirect
11 import subprocess
12
13 # přesměrování
14 @route('/')
15 def redir():
16 redirect("/login")
17
18 @get('/login') # nebo @route('/login')
19 def login():
20 print '---------------------'
21 print request.method
22 print '---------------------'
23 return '''
24 <form action="/login" method="post">
25 Username: <input name="username" type="text" />
26 Password: <input name="password" type="password" />
27 <input value="Login" type="submit" />
28 </form>
29 '''
30
31 @post('/login') # nebo @route('/login', method='POST')
32 def do_login():
33 print '---------------------'
34 print request.method
35 # print dir(request.params)
36 for klic in request.params.keys():
37 print klic,'->',request.params[klic]
38 print '---------------------'
39 username = request.forms.get('username')
40 password = request.forms.get('password')
41 if 'abcd' in password :
42 return """
43 <h1>{0}</h1>
44 <p>Hurá! Huráááá. Uhodl jsi</p>
45 """.format(username)
46 else :
47 return "<h1>Ne! Ne! Ne!!!</h1><p>Špatné heslo :-P</p>"
48
49
50 @route('/pwgen')
51 def pwgen():
52 h='<h1>Zapamatovatelné heslo?</h1>\n'
53 form='''<form method="get">
54 <p>
55 Délka hesla (od 5 do 40): <input name="length" type="text" /><br />
56 <input value="goo" type="submit" />
57 </p>
58 </form>
59 '''
60
61 length = request.query.length
62 if length :
63 try :
64 length=int(length)
65 length= 8 if length<5 or length>40 else length
66 except :
67 length=8
68 pswd= subprocess.check_output(['pwgen','-Ccn',str(length), ])
69 return h+'\n<p>Délka '+str(length)+'</p>\n<pre style="font-size:large;">\n'+pswd+'</pre>\n'+form
70 else :
71 return h+form
72
73 ##########################################################################
74 run(host='localhost', port=8090, debug=True, reloader=True)
`--> stáhnout
Opět vyzkoušejte jednotlivá URL a prozkoumejte zdrojový kód k nim příslušející.
Webové aplikace napsané v Pythonu mají sklon „vlastnit“ celý adresář (nebo i celou doménu). Přiřazování konkrétních URL určitému kódu je tedy o něco pružnější a je obvykle spravováno tzv. routingem1.
Někdy se ale přece jen hodí mít některé části webu umístěny ve statických souborech. Typickým příkladem můžou být obrázky nebo CSS. V těchto případech použijeme následující kód.
from bottle import static_file
@route('/static/<filename>')
def server_static(filename):
return static_file(filename, root='/path/to/your/static/files')
`--> stáhnout
nebo lépe:
@route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/path/to/your/static/files')
`--> stáhnout
Funkce static_file() je obecně doporučovanou cestou pro předávání statický
souborů. Funkce
může automaticky odhadnout MIME
daného souboru. Ale můžeme to i přímo určit.
from bottle import static_file
@route('/images/<filename:re:.*\.png>')
def send_image(filename):
return static_file(filename, root='/path/to/image/files', mimetype='image/png')
@route('/static/<filename:path>')
def send_static(filename):
return static_file(filename, root='/path/to/static/files')
`--> stáhnout
Prohlížeče většinu souboru jejichž typ znají zobrazí. Je to například PDF nebo JPEG. Dialog pro stažení lze vynutit pomocí parametru
download. Ten může obsahuje jméno souboru. Pokud se rozhodneme ho nechat
stejné jako na serveru stačí hodnotu nastavit na True.
@route('/download/<filename:path>')
def download(filename):
return static_file(filename, root='/path/to/static/files', download=filename)
`--> stáhnout
Vytvářet stránku vždy přímo v obslužné funkci by bylo hodně nepohodlné. Proto Bottle přichází s jednoduchým šablonovacím systémem. Pokud ale tento vestavěný šablonovací systém nedostačuje je možné použít robustnější řešení jako je mako, jinja2 nebo cheetah.
Bottle šablony hledá v adresáři ./views/ (respektive v seznamu
bottle.TEMPLATE_PATH).
Šablona může vypadat například takto:
%if name == 'World':
<h1>Hello {{name}}!</h1>
<p>This is a test.</p>
%else:
<h1>Hello {{name.title()}}!</h1>
<p>How are you?</p>
%end
`--> stáhnout
A následující kód slouží pro použití šablony.
@route('/hello')
@route('/hello/<name>')
def hello(name='World'):
return template('hello_template', name=name)
`--> stáhnout
Alternativní způsob rendrování šablony je decorator @view. Obslužná funkce
potom musí vracet dict s proměnnými a jejich hodnotami.
@route('/hello')
@route('/hello/<name>')
@view('hello_template')
def hello(name='World'):
return dict(name=name)
`--> stáhnout
Pokud se něco pokazí Bottle automaticky zobrazí chybovou stránky. Chybovou stránku si ale můžeme pro konkrétní HTTP status code vytvořit sami.
Například status 404 říká, že dokument nelze nalézt:
from bottle import error
@error(404)
def error404(error):
return 'Nic tu není, jdi pryč'
`--> stáhnout
Pokud z nějakého důvodu chceme chybový stav programově vyvolat poslouží nám k
tomu funkce abort().
from bottle import route, abort
@route('/restricted')
def restricted():
abort(401, "Promiň, ale sem nesmíš.")
`--> stáhnout
@error(404)
def notFound(error):
r='<h1>'+error.status+'</h1>'
r+='<p>Sorry. Tady nic není</p><hr />'
r+='<p>'+error.body+'</p>'
return r
@route('/nic')
def nic():
abort(404,'bbbbeeeeeeeeeee')
`--> stáhnout
Vyzkoušejte si rozdíl v chování pro adresu /nic a /nejakablbost_nmfioewkwfjweioq
Funkce redirect() vyvolá status 303 See Other a přesměruje požadavek na
jinou stránku.
from bottle import redirect
@route('/spatna/url')
def wrong():
redirect("/spravna/url")
`--> stáhnout
Všechny HTTP hlavičky zaslané klientem (např. Referer, Agent or Accept-Language)
jsou dostupné
ve slovníkovém objektu request.headers.
@get('/req')
def req():
r='<pre>'
for k in request.headers.keys() :
r += k+':'+request.headers[k]+'\n'
r+='</pre>'
return r
`--> stáhnout
Pokud chceme zasáhnou do HTTP hlavičky,
kterou naše aplikace odesílá, použijeme objekt Response.
@route('/wiki/<page>')
def wiki(page):
response.set_header('Content-Language', 'en')
...
`--> stáhnout
Pokud má hlavička obsahovat více stejnojmenných polí použijeme kromě
metody .set_header() také metodu add_header.().
....
response.set_header('Set-Cookie', 'name=value')
response.add_header('Set-Cookie', 'name2=value2')
....
`--> stáhnout
Změna výchozího kódování se děje pomocí pole Content-Type. Výchozí hodnotu:
Content-Type: text/html; charset=UTF-8
... je možné změnit pomocí Response.content_type nebo přímo pomocí
Response.charset:
from bottle import response
@route('/iso')
def get_iso():
response.charset = 'ISO-8859-15'
return u'This will be sent with ISO-8859-15 encoding.'
@route('/latin9')
def get_latin():
response.content_type = 'text/html; charset=latin9'
return u'ISO-8859-15 is also known as latin9.'
`--> stáhnout
Některé informace se WSGI/CGI programu předávájí pomocí
proměnných prostředí. Je to například REQUEST_METHOD nebo REMOTE_ADDR.
@route('/my_ip')
def show_ip():
ip = request.environ.get('REMOTE_ADDR')
# or ip = request.get('REMOTE_ADDR')
# or ip = request['REMOTE_ADDR']
return template("Your IP is: {{ip}}", ip=ip)
`--> stáhnout
nebo
@get('/env')
def env():
r='<pre>'
for k in request.environ.keys() :
r += '<strong>'+k+'</strong>:'+str(request.environ[k])+'\n\n'
r+='</pre>'
return r
`--> stáhnout
Cookie je pojmenovaný kus textu uložené ve webovém prohlížeče. Cookie si
můžeme vyžádat přes request.get_cookie() a nastavit nové cookies pomocí
response.set_cookie():
@route('/hello')
def hello_again():
if request.get_cookie("visited"):
return "Vítej zpět! Je jezké, že jsi zase přišel"
else:
response.set_cookie("visited", "yes")
return "Nazdar! Vítam tě tu."
`--> stáhnout
Metoda response.set_cookie() přijímá řadu dalších pojmenovaných argumentů,
které řídí život a chování souborů cookie. Některé z nejběžnějších nastavení
jsou popsány v následující tabulce:
max_age |
Platnost cookie v sekundách. (default: None) |
expires |
Expirace: datetime objekt nebo UNIX timestamp. (default: None) |
domain |
Doména pro kterou cookie platí (default: aktuální doména). |
path |
Limituje cookie pro určitou cestu (default: /). |
secure |
Limituje cookie pro HTTPS spojení (default: off). |
httponly |
Zabrání Jabascriptu na straně klienta ve čtení cookie (default: off). |
Pokud není nastaven expires ani max_age, cookie vyprší na konci relace
prohlížeče.
Dále byste měli vzít v úvahu:
Více o routingu se dozvíte v oficiální dokumentaci na http://bottlepy.org/docs/dev/tutorial.html#request-routing a http://bottlepy.org/docs/dev/routing.html ↩