Repository 32bit  Forum
Repository 64bit  Wiki

Pylons quick reference: differenze tra le versioni

Da Slacky.eu.
(Form validators)
(Impostare WSGI per apache)
 
Riga 258: Riga 258:
DocumentRoot /home/utente/PYLONS/myproj
DocumentRoot /home/utente/PYLONS/myproj
WSGIScriptAlias / /home/utente/PYLONS/myproj/mysite.wsgi
WSGIScriptAlias / /home/utente/PYLONS/myproj/mysite.wsgi
 
</VirtualHost>
</VirtualHost>
</pre>
</pre>
  +
= Conclusioni =
= 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.
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.

Versione attuale delle 03:05, 12 mag 2008

Indice

[modifica] Inizio

[modifica] 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.

[modifica] 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.

[modifica] 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.

[modifica] Sviluppo progetto

[modifica] 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')

[modifica] 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)

[modifica] 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

[modifica] 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()

[modifica] 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')

[modifica] 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' )
              ]

[modifica] 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>

[modifica] 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 !

[modifica] Links

http://pylonshq.com/

http://pylonshq.com/WebHelpers/

http://www.makotemplates.org/docs/

http://formencode.org/

http://routes.groovie.org/manual.html

http://www.webwareforpython.org/DBUtils/Docs/UsersGuide.html

http://script.aculo.us/


Autore: Antonio67

Data: Mon May 12 2008

Strumenti personali
Namespace

Varianti