Ultimamente ho avuto la necessità di sviluppare un’applicazione web. Dando un’occhiata – forse più di una – a ciò che offre l’attuale tecnologia mi sono imbattuto in numerosi web framework (vedi la pagina inglese per maggiori dettagli).
Avendo in passato avuto modo di vedere Django e Turbogears, forse i due più noti web framework per Python, e sapendo già quanto siano potenti e avanzati, ho pensato di dare un’occhiata a tecnologie basate su Java.
Struts, che tutti consigliano, mi è sembrata una buona tecnologia, stabile e potente, ma anch’essa troppo avanzata per il progetto che mi sono proposto di fare. Anzi, forse è più complessa rispetto alle altre due già nominate in precedenza e poiché un buon principio della programmazione è quello di non aggiungere complessità se non ne hai bisogno ho preferito evitare.
Allora mi sono ricordato di un framework minimalista sviluppato in Python chiamato web.py.
Installazione
1) Sui sistemi GNU/Linux probabilmente quasi tutti i repository contengono questo pacchetto. Vi basterà cercarlo come “webpy” e installarlo. Su Ubuntu ad esempio vi basterà digitare:
sudo apt-get install python-webpy
2) Sulla maggior parte dei sistemi operativi, se avete installato easy_install (è un programma contenuto in setuptools) vi basterà lanciare il comando:
easy_install web.py
3) Se non avete easy_install (male, vi consiglio di installarlo subito!), seguite le istruzioni del sito.
Configurazione
Personalmente preferisco usare un sistema dei template diverso da quello standard di webpy. Mi sono abituato infatti a un sistema chiamato Jinja 2, probabilmente per la somiglianza con quello incluso in Django, che ho già usato in passato.
Per installare questo nuovo sistema di template (fatelo, altrimenti non vi funzioneranno gli esempi!), usate easy_install (ecco perché lo consiglio
):
easy_install Jinja2==dev
…And The Winner Is…!!
Una volta installati webpy e jinja2, per verificare che l’installazione sia andata a buon fine, aprite un interprete Python e digitate:
Se viene sollevato un ImportError, riprovate a installarli.
Primi passi
Come prima applicazione, vedremo come dare il benvenuto ai visitatori!
Innanzitutto creiamo la nostra directory di esempi. La chiameremo sandbox.
Create un’altra cartella all’interno, chiamatela example1. All’interno di quest’ultima cartella, create un file e salvatelo come first_example.py.
Il file conterrà il seguente codice:
import web
# configuriamo i path di cui si occupera' la nostra applicazione
urls = (
'/', 'Index'
)
# istanziamo l'applicazione con gli URL configurati
app = web.application(urls, globals())
# la view che e' stata associata al path '/'
class Index(object):
def GET(self):
return 'Benvenuto visitatore!'
if __name__ == '__main__':
# avviamo il server di sviluppo di webpy
app.run()
Dopo aver importato il modulo di webpy, chiamato web, abbiamo bisogno di specificare al programma quale debba essere il path che l’utente dovrà inserire nel browser per avere il nostro messaggio di benvenuto. Chiaramente, poiché si tratta di un esempio, imposteremo solo la radice, ovvero /, così che quando l’utente si collegherà al nostro server web, per esempio http://myserverweb/, visualizzerà il messaggio di benvenuto.
Dopo aver specificato quale path rendere pubblico all’utente, dobbiamo creare un’istanza dell’applicazione principale poiché come vedremo più avanti è possibile sviluppare più applicazioni e includerle nello stesso server web.
Vi starete chiedendo perché viene usato globals(). Semplicemente abbiamo bisogno di dire alla nostra applicazione da dove deve prendere la classe Index che gli abbiamo indicato nella tupla urls. Se Index fosse stata definita in un file esterno, esempio index.py, avremmo potuto omettere la chiamata a funzione globals() semplicemente importandola dal modulo index.py.
Comunque, per quanto riguarda la classe Index, essa contiene un metodo, GET, che non fa altro che rispondere alle richieste di tipo GET. (Per le differenze tra GET e POST vedi qui). La classe Index rappresenta ciò che comunemente nel modello MTV (quello usato da Django, in poche parole MVC con una sigla diversa) è indicato dalla view, ovvero la callback che presenta i dati che l’utente deve visualizzare.
Nel nostro caso abbiamo associato l’oggetto Index al path ‘/’.
Il messaggio che sarà mostrato è Benvenuto visitatore!
Infine decidiamo di eseguire la nostra app. Basta un semplice .run() et voilà.
Spostiamoci nella cartella sandbox/example1 e lanciamo il comando:
python first_example.py
Se tutto è andato a buon fine, vi verrà mostrata una scritta sul terminale, http://0.0.0.0:8080/ , che indica l’host e la porta sui quali il server (di webpy!) è in ascolto.
Aprite dunque il vostro browser e digitate quell’indirizzo nella barra degli URL.
Ok, e ora?
Non vi ho certamente fatto installare Jinja inutilmente!
Nell’esempio seguente vedremo come dare il benvenuto a un singolo utente, che però dovrà dirci come si chiama.
Innanzitutto creiamo un’altra cartella in sandbox/, chiamiamola example2. Apriamo un file, nominiamolo mainapp.py e incolliamo il seguente codice:
import web
import urls
app = web.application(urls.mapping)
if __name__ == '__main__':
app.run()
Questa volta il codice del nostro main è ridotto, poichè abbiamo deciso di suddividere l’applicazione in più moduli e package. Per questo motivo urls.py sarà diverso:
from views import index, greeting
urls = (
'/', index.Index,
'/greet_user/(\w+)', greeting.Greeting,
)
Abbiamo due URL da configurare: / e /greet_user che serviranno rispettivamente l’uno per mostrare il form per l’inserimento del nome e l’altro per salutare l’utente.
Qualcuno potrebbe chiedersi cosa significhi (\w+) in coda al secondo path. Si tratta di una regex. (Per informazioni, date un’occhiata al modulo re di Python). Tale regex corrisponde a ogni carattere alfanumerico presente dopo il path /greet_user/. In questo modo se come indirizzo scriviamo /greet_user/mario, applicando la regex sull’URL otterremo come risultato la parola mario. Chiaramente è una funzionalità non banale di webpy che ci permette di gestire gli URL dinamicamente. Come avremmo fatto altrimenti a dire che vogliamo associare una view a tutti i caratteri alfanumerici che vengono dopo /greet_user/?
Le parentesi intorno alla regex indicano che si tratta di un solo gruppo di caratteri, il che significa che quando sarà applicata la regex alla stringa dopo greet_user/, se ci sarà una corrispondenza essa sarà costituita da una sola stringa. A questo punto bisognerà in qualche modo dire al server quale sia stato il match, ovvero la stringa corrispondente alla regex. Ad occuparsi di ciò è ancora una volta webpy. L’unica cosa che noi dobbiamo fare è aggiungere un solo argomento al metodo usato (es. GET). Se ci fossero stati più gruppi, dunque più stringhe “separate”, avremmo dovuto aggiungere un numero uguale di argomenti al metodo. (Più avanti vedremo l’esempio).
Ora creiamo due directory nella cartella principale: templates e views. All’interno di views/ creiamo tre file: index.py, greeting.py e __init__.py.
In index.py inseriamo il seguente codice:
import web
from utils import render
# un semplice form
name_form = web.form.Form(
web.form.Textbox('name', web.form.notnull, description='Name')
)
class Index(object):
def GET(self):
f = name_form()
return render.index(form=f)
def POST(self):
f = name_form()
if not f.validates():
return render.index(form=f)
else:
# effettua un redirect alla pagina di saluto
web.seeother('/greet_user/%s' %f.name.get_value())
Nella creazione del form abbiamo specificato che non vogliamo che l’utente lo lasci vuoto, con il flag web.form.notnull. Inoltre abbiamo aggiunto la descrizione, ovvero un breve testo che sarà visualizzato accanto al form.
La view Index definisce due funzioni: GET e POST. Quando accediamo alla pagina / stiamo inviando una richiesta di tipo GET al server. Tale richiesta sarà gestita dal metodo GET che non farà altro che renderizzare il template con il form appena creato. Quando inseriamo un nome nel form e premiamo invio stiamo inviando una richiesta di tipo POST al server. Tale richiesta sarà gestita proprio dal metodo POST sopra definito.
Se lasciamo il form vuoto e premiamo invio, il metodo POST definito si occuperà di restituire un nuovo form da compilare, specificando che non è valido. Altrimenti saremo reindirizzati alla pagina di saluto.
Il template da visualizzare è il seguente.
<div id="login-box">
<form name="login" method="post">
{% if not form.valid %}
<p>DEVI INSERIRE UN NOME!
</p>
{{ form.render() }}
{% else %}
<p>Per favore, inserisci un nome
</p>
{{ form.render() }}
{% endif %}
</div>
Salviamolo in templates/ come index.html.
Quando viene richiesta la pagina index.html (metodo GET!!), viene creato un form al quale è associato il metodo post e in più viene generato il codice dei vari input che compongono il form, creato dalla view Index. Dopo aver inserito il nome e premuto invio, effettuiamo una richiesta di tipo POST. Se i campi sono stati compilati correttamente saremo reindirizzati alla pagina greet_user/$USER, dove $USER è il nome dell’utente inserito, altrimenti saremo reindirizzati alla stessa pagina, che ci suggerirà in maniera decisa di inserire un nome. Tale logica viene descritta usando il sistema dei template jinja. Non è difficile capire che i controlli si trovano all’interno di parentesi graffe con i simboli delle percentuali mentre le chiamate a funzione o le variabili si trovano in parentesi graffe doppie.
Diamo un’occhiata a greeting.py.
from utils import render
class Greeting(object):
# nota che questa volta GET accetta un parametro!
# cio' significa che al nostro URL va aggiunto del testo aggiuntivo
# esempio: /greeting/mario
def GET(self, name):
return render.greeting(uname=name)
Come è stato già spiegato precedentemente, poiché in urls.py abbiamo associato la view Greeting all’url greet_user/(\w+) ora c’è bisogno di aggiungere un parametro al metodo GET. Abbiamo chiamato tale parametro name. Vediamo come sarà usato nel template greeting.html, in templates/.
<div id="greeting">
<p>Ciao {{ uname }}!
</p>
</div>
Ed ecco fatto! Abbiamo incluso tra le parentesi graffe doppie il nome del parametro che sarà passato dalla view.
Il modulo utils lo trovate qui. Salvatelo nella directory example2/ e infine lanciate il server, mainapp.py e testate l’applicazione!
Riferimenti esterni
- webpy : contiene esempi di applicazioni e informazioni varie
- jinja2: spiega come sfruttare a pieno il sistema dei template
Il codice è disponibile sul repository webpy-examples.