Pylons quick reference
Indice
Inizio
Introduzione
Dopo aver provato nell'ordine mod_python, vampire e Django, ed essere rimasto per vari motivi insoddisfatto di tutti e tre, ho scoperto Pylons che, secondo me, e' un perfetto equilibrio tra il controllo dell'applicazione e le facilitazioni che il framework mette a disposizione dello sviluppatore.
Infatti il framework non fa altro che riutilizzare i migliori pezzi di altri progetti: Formencode, Routes e i Webhelpers.
Questa vuol essere una breve e concisa guida all'utilizzo del framework.
Installazione
Una volta scompattato il .tar.gz, basta spostarsi nella directory cosi' creata e dare il comando:
# python setup.py install
Il comando installera' i componenti necessari nella directory site-packages delle librerie python.
Inizializzazione di un progetto
Per creare la struttura della directory del progetto, basta dare il comando:
$ paster create -t pylons nomeproj
E possiamo subito dopo lanciare l'applicazione !
$ cd nomeproj $ paster serve --reload development.ini
Il comando lancia un server WSGI sulla porta 5000, alla quale potremmo collegarci con un qualsiasi browser all'indirizzo http://127.0.0.1:5000. Una volta passato il "test", eliminiamo poi dalla directory public il file index.html.
Sviluppo progetto
Creare un controller
Un controller non e' altro che un modulo python contenente una classe, che "risponde" alla richiesta di una determinata URL. Ogni controller ha uno o piu' action, che non sono altro che metodi della classe stessa. L'action di default, se non specificata, e' il metodo index.
Per la creazione del primo controller della nostra applicazione, dare il comando:
$ paster controller start
Il comando creera' un modulo nella sottodirectory controller/ e uno in test/.
Per visualizzare la "pagina" appena creata, bastera' aprire http://127.0.0.1/start, che mostrera' il classico Hello World. Se vogliamo che questa diventi la nostra pagina di default, dobbiamo variare il file config/routing.py aggiungendo, prima della riga standard, la connect al controller:
map.connect('', controller='start') map.connect(':controller/:action/:id')
REST controller
Se invece vogliamo utilizzare lo schema REST per i nostri controller, basta dare il comando:
$ paster restcontroller nave navi
Il comando crea il controller nave.py, che viene reso accessibile aggiungendo in config/routing.py la riga:
map.resource('nave', 'navi')
I vari metodi creati per accedere agli items sono autoesplicativi:
def index(self, format='html'): """GET /nave: All items in the collection.""" # url_for('navi') def create(self): """POST /nave: Create a new item.""" # url_for('navi') def new(self, format='html'): """GET /navi/new: Form to create a new item.""" # url_for('new_nave') def update(self, id): """PUT /navi/id: Update an existing item.""" # Forms posted to this method should contain a hidden field: # <input type="hidden" name="_method" value="PUT" /> # Or using helpers: # h.form(h.url_for('nave', id=ID), # method='put') # url_for('nave', id=ID) def delete(self, id): """DELETE /navi/id: Delete an existing item.""" # Forms posted to this method should contain a hidden field: # <input type="hidden" name="_method" value="DELETE" /> # Or using helpers: # h.form(h.url_for('nave', id=ID), # method='delete') # url_for('nave', id=ID) def show(self, id, format='html'): """GET /navi/id: Show a specific item.""" # url_for('nave', id=ID) def edit(self, id, format='html'): """GET /nave/id;edit: Form to edit an existing item.""" # url_for('edit_nave', id=ID)
Autenticazione
Il metodo piu' semplice per gestire l'autenticazione e' quello di inserire in lib/base.py un metodo __before__ nella classe BaseController, da cui "derivano" tutte le classi controller.
class BaseController(WSGIController): requires_auth = False def __before__(self): if self.requires_auth and 'user' not in session: # remember where we came ... session['path_before_login'] = request.path_info session.save() return redirect_to(h.url_for(controller='login'))
In login.py bastera' autenticare l'user con un form e poi fare il redirect:
''' autentica ''' ... session['user'] = _res[0] session.save() if session.get('path_before_login'): redirect_to(session['path_before_login']) else: redirect_to('/')
Nelle classi che richiedono l'autenticazione, bastera' settare
requires_auth = True
Connessione a PostgreSQL con DBUtils
Se preferite usare direttamente l'SQL di psycopg2, invece di SQLAlchemy, dovete impostare una variabile globale per la connessione, in modo che possa essere richiamata in ogni modulo/controller dell'applicazione. Per fare questo, editare il file lib/app_globals.py ed aggiungere le righe:
import psycopg2 from DBUtils.PooledDB import PooledDB class Globals(object): def __init__(self): app_conf = config['app_conf'] self.pool = PooledDB ( psycopg2, mincached=2, maxconnections=5, maxshared=5, database=app_conf['pool.database'], user=app_conf['pool.user'], password=app_conf['pool.password'] )
Impostare poi in development.ini:
[app:main] pool.database = 'miodb' pool.user = 'utente' pool.password = 'secret'
La connessione puo' poi essere richiamata in ogni controller:
def salva(self): try: _db = g.pool.connection() _crs = _db.cursor() _crs.execute('''INSERT INTO .... _db.commit() except Exception, error: _db.rollback() finally: _crs.close() _db.close()
Form validators
I moduli delle librerie formencode facilitano moltissimo la gestione della validazione dell'input dei form HTML. La procedura per creare la validazione del form e' semplice e pulita, separando completamente la parte del controllo dall'applicazione stessa, che viene "decorata" per indicare lo schema e il form di riferimento, a cui eventualmente passare i messaggi di errore.
Per procedere, creare il file model/form_schema.py:
import formencode class MyForm(formencode.Schema): # altrimenti da errore, ad esempio, per il campo # submit del form. # allow_extra_fields = True # pero' elimina gli extra_fields dai valori ritornati ! # filter_extra_fields = True nome = formencode.validators.NotEmpty() cogn = formencode.validators.MaxLength(30, not_empty=True)
Nella funzione della classe dove deve essere effettuato il controllo e il salvataggio del submit del form, basta indicare nel decorator lo schema da utilizzare per la validazione e l'action da richiamare in caso di errore. La key variable_decode serve per "tradurre" gli eventuali parametri passati in forma di list o di dict.
Per passare una serie di parametri in forma di list, basta chiamare la variabile nel formato nomelist-<numero>, mentre per utilizzare un dict basta chiamare la variabile nomedict.<key>.
from fileserver.model.form_schema import MyForm class MyController(BaseController): def index(self): return render('/form.tmpl') @validate(schema=MyForm(), form='index', variable_decode=True) def salva(self): try: ... except Exception, error: _db.rollback() return render('/saved.tmpl')
Ancora validators
Se abbiamo creato nel form HTML una lista/dict di variabili da passare al validator, basta utilizzare formencode.foreach.ForEach() richiamando la classe che setta e controlla i validators per le key del dict.
class Numeri(formencode.Schema): t_id = formencode.validators.Int() descrizione = formencode.validators.String() class Dati(formencode.Schema): allow_extra_fields = True filter_extra_fields = True id = formencode.validators.Int(not_empty=True) numeri = formencode.foreach.ForEach(Numeri())
Per controllare la presenza/assenza di un campo a seconda della presenza/assenza di un altro, si utilizza chained_validators con la classe RequireIfMissing(). La classe prende due dei tre parametri: required e missing oppure required e present:
chained_validators = [ formencode.validators.RequireIfMissing ( missing='societa', required='cognome' ) ]
Impostare WSGI per apache
Per usare apache come frontend WSGI al programma Pylons, creare nella directory del progetto un file (ad esempio mysite.wsgi) contenente:
import os, sys sys.path.append('/home/utente/PYLONS/myproj') os.environ['PYTHON_EGG_CACHE'] = '/home/utente/PYLONS/python-eggs' from paste.deploy import loadapp application = loadapp('config:/home/utente/PYLONS/myproj/development.ini')
La sottodirectory data deve essere scrivibile dall'utente apache, quindi o settiamo i permessi in modo che l'user impostato in httpd.conf possa scrivere/leggere:
# chgrp -R apache /home/utente/PYLONS/myproj/data # find data -type d -exec chmod 0770 {} \; # find data -type f -exec chmod 0660 {} \;
Oppure semplicemente la rimuoviamo, in modo che venga ricreata alla connessione ad apache.
Nel file di configurazione di Apache httpd.conf settare i permessi di accesso alla directory:
<Directory "/home/utente/PYLONS/myproj"> Order deny,allow Allow from all </Directory>
Mentre in extra/httpd-vhosts.conf creare il VirtualHost:
<VirtualHost *:80> ServerName myproj.dominio.mio ServerAdmin ilmio@indirizzo.mail DocumentRoot /home/utente/PYLONS/myproj WSGIScriptAlias / /home/utente/PYLONS/myproj/mysite.wsgi </VirtualHost>
Conclusioni
La scelta di un framework per lo sviluppo di applicazioni web e' sicuramente un fatto personalissimo, almeno quanto la scelta di una distribuzione Linux ! Per questo vorrei riportare brevemente l'esperienza che mi ha portato a scegliere Pylons rispetto ad altri progetti.
mod_python o mod_wsgi richiedono una preparazione di partenza di superclassi e di moduli personali per gestire centralmente l'applicazione. Questo comporta una disponibilita' di tempo iniziale per la pianificazione del codice, e un tempo successivo alla stesura dell'applicazione stessa per ripulirlo, riunificando e generalizzando le parti che (mooolto probabilmente !) ci saranno sfuggite al momento del lavoro su carta.
Django e' per me stata una delusione assoluta ! All'inizio sono rimasto entusiasta da admin e tutto il resto, ma sono rimasto fortemente scottato quando sono andato a riprendere, dopo 6 mesi, il codice in mano. Saro' lento io, ma riprendere in mano la situazione e' stato quanto meno dispendioso in termini di tempo. Sicuramente colpa mia e della mia applicazione, ma diciamo che la mia forma mentis non e' per niente in sintonia cone quel genere di framework.
Pylons mi sembra una scelta equilibrata sotto tutti i punti di vista, sia per la struttura del progetto, che permette di sapere sempre dove sono i vari elementi dell'applicazione, sia per la liberta' di implementazione che lascia. Per la gestione del database puo' essere usato qualsiasi libreria, e lo stesso dicasi per i templates (anche se Mako lo giudico un must del genere !). E poi, da buon ultima, la spettacolare semplicita' dello sviluppo di AJAX con gli helper e la libreria script.aculo.us !
Links
http://pylonshq.com/WebHelpers/
http://www.makotemplates.org/docs/
http://routes.groovie.org/manual.html
http://www.webwareforpython.org/DBUtils/Docs/UsersGuide.html
Autore: Antonio67
Data: Mon May 12 2008