Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
90/222
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 ↩