11.2.2 Usare il modulo cgi

Si inizia scrivendo "import cgi". Non si deve usare "from cgi import *" dato che il modulo definisce tutti i tipi di nomi che userà o per compatibilità con il passato, che non si vorranno nello spazio dei nomi che si sta usando.

Quando si scrive un nuovo script, occorre considerare l'aggiunta della seguente riga:

import cgitb; cgitb.enable()

Questo attiva uno speciale gestore per le eccezioni che mostra nel browser resoconti dettagliati se dovesse insorgere un errore. Se non si desidera mostrare queste informazioni all'utente del proprio script, si può decidere di salvare il resoconto in un file, con una riga simile a questa:

import cgitb; cgitb.enable(display=0, logdir="/tmp")

È molto utile utilizzare questa funzionalità durante la stesura dello script. Il resoconto prodotto da cgitb fornisce informazioni che possono far risparmiare tanto tempo nella ricerca dei bug. Si può semplicemente rimuovere successivamente la riga cgitb quando lo script è stato adeguatamente testato e si è sicuri che funzioni correttamente.

Per ottenere le informazioni inserite attraverso la form, è preferibile utilizzare la classe FieldStorage. Le altre classi definite in questo modulo, sono presenti soprattutto per compatibilità all'indietro. Istanziarla esattamente una sola volta, senza argomenti. In questo modo verà letto il contenuto della form dallo standard input o dalle variabili ambiente (questo dipende dal valore di diverse variabili d'ambiente, in accordo con lo standard CGI). Dato che può sprecare dello standard input, deve essere istanziata solo una volta.

L'istanza FieldStorage può essere indicizzata come un dizionario Python, e supporta anche i metodi standard per i dizionari has_key() e keys(). È supportata anche la funzione built-in len(). I campi della form che contengono stringhe vuote vengono ignorate e non appaiono nel dizionario; per catturare questi campi, si deve fornire un valore True al parametro chiave facoltativo keep_blank_values quando si crea un'istanza di classe FieldStorage.

Per esempio, il seguente codice (che assume che l'header Content-Type: ed una riga vuota siano già state stampate), controlla che i campi name e addr siano entrambi impostati a stringhe non vuote:

form = cgi.FieldStorage()
if not (form.has_key("name") and form.has_key("addr")):
    print "<H1>Errore</H1>"
    print "Per favore, inserire un nome ed un indirizzo valido nei campi."
    return
print "<p>nome:", form["name"].value
print "<p>indirizzo:", form["addr"].value
... Altre azioni da eseguire qui ...

Qui i campi, accessibili attraverso "form[chiave]", sono a loro volta istanze di FieldStorage (o MiniFieldStorage, in base alla codifica della form). L'attributo value dell'istanza contiene il valore stringa del campo. Il metodo getvalue() restituisce direttamente il valore della stringa; accetta anche un secondo argomento facoltativo come predefinito da restituire se la chiave richiesta non è presente.

Se i dati immessi nella form contengono più di un campo con il medesimo nome, l'oggetto recuperato da "form[chiave]" non è una istanza di FieldStorage o MiniFieldStorage ma una lista di queste istanze. Analogamente, in questa situazione, "form.getvalue(chiave)" potrebbe restituire una lista di stringhe. Se ci si aspetta questa possibilità (quando la propria form HTML contiene campi multipli con lo stesso nome), utilizzare la funzione built-in isinstance() per determinare quando si ha a che fare con singole istanze o liste di istanze. Per esempio questo codice concatena un numero imprecisato di campi nomeutente, separati da una virgola:

value = form.getvalue("nomeutente", "")
if isinstance(value, list):
    # Campi multipli nomeutente specificati
    usernames = ",".join(value)
else:
    # Singolo o nessun campo nomeutente specificato
    usernames = value

Se il campo rappresenta un file caricato via upload, l'accesso al valore tramite l'attributo value o attraverso il metodo getvalue() legge in memoria l'intero file come una stringa. Questo può non essere ciò che si vuole. Si può verificare se si tratta di un file caricato controllando o l'attributo filename o l'attributo file. Si possono quindi leggere i dati contenuti attraverso l'attributo file.

fileitem = form["userfile"]
if fileitem.file:
    # E` un file caricato via upload; si contano le righe
    linecount = 0
    while 1:
        line = fileitem.file.readline()
        if not line: break
        linecount = linecount + 1

Lo standard per l'upload dei file descrive la possibilità di inviare file multipli da un singolo campo (usando una codifica ricorsiva multipart/*). Quando questo avviene, l'elemento sarà simile ad un dizionario FieldStorage. Questo si può determinare controllando il suo attributo type, che dovrebbe essere multipart/form-data (o, al massimo, un altro tipo MIME corrispondente a multipart/*). In questo caso, può essere iterato ricorsivamente semplicemente come un oggetto di alto livello della form.

Quando una form viene immessa attraverso il ``vecchio'' formato (come una stringa di ricerca o come un singolo elemento di dati del tipo application/x-www-form-urlencoded), l'elemento sarà attualmente una istanza della classe MiniFieldStorage. In questo caso gli attributi list, file e filename sono sempre None.

Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.