El Blog de Trespams

Blog personal sobre tecnologia, gestió de projectes i coses que se me passen pel cap

Avaluació pererosa a Python: un exemple amb Django

Aprofitant que tenc vacances (eps! una setmana i ja s'acaben) aprofitaré per comentar un codi que serveix tant per entendre un poc més Python com per fer feina amb Django. La idea és del blog themorge.org


class PersonForm(ModelForm):
    "Simple form for the Person model"
    class Meta:
        model = Person

def edit(request,id=None):
    """Edits the Person model"""
    formulario = PersonForm(request.POST or None, 
                instance = id and Person.objects.get(id = id) )
    if request.method == 'POST':
        if formulario.is_valid():
            persona = formulario.save()
            return HttpResponseRedirect('/fitxa/guardada/%s/'%persona.id)
    return render_to_response('edit.html', {'formulario': formulario})

Això representa un formulari d'edició amb Django (versió trunk). Tenim definit als models de l'aplicació una classe Person i el que volem és crear un formulari d'edició a partir d'aquest model.

A la classe PersonForm podem veure com hem creat el formulari a partir de la definició del model. Podem personalitzar el formulari afegint-hi validacions específiques, canviar la manera que s'edita etc, però si volem una cosa bruta i ràpida amb quatre línies en sortim (sí, la documentació sí és necessària).

Definim ara la part d'edició en sí, en el meu cas l'he anomenada edit, i fa referència a una plantilla edit.html, que bàsicament conté

       <form action="." method="post">
           <table>
               {{formulario}}
           </table>
           <button name="submit" value="submit"  type="submit">Send</button>
           <button name="reset" value="reset" type="reset">Reset</button>
       </form>
   

L'embolcall de l'html ho deixo com a exercici pel lector.

Anem a veure què passa quan a través de la url anam a parar a l'edit. Tenim dos casos possibles inicialment, que vulguem afegir un mou registre o que en volguem modificar-ne un ja existent. En el primer cas se'ns ha de mostrar un formulari en blanc, i en el segon un formulari amb les dades omplertes del l'objecte Persona agafat de la base de dades per l'ORM de Django.

Els urls que jo he definit són

     (r'^edit/(?P\d+)/$','edit'),
     (r'^add/$','edit'),

Com veim hi ha dues urls que apunten al mètode edit, la primera agafa l'id com a paràmetre, per la qual coses les urls serien de la forma /edit/10/ i la segona no agafa cap paràmetre i per tant queda com /add/. De fet podríem no haver fet la distinció entre edit i add, però si la feim el codi html ens quedarà molt més bo de llegir, més semàntic.

Estudiem el cas add. Quan entrem al mètode edit, com que no li passam l'id agafarà el valor per defecte, a saber None ja que així ho me definit al mètode, i ara comença la part interessant...

Definim un objecte de tipus PersonForm, la inicialització de la classe agafa dos paràmetres, el primer són els valors inicials del formulari i el segon és la instància de la classe que volem modificar.

Quan seleccionam editar un registre o afegir-ne un de nou, el que feim és un GET i quan guardam el registre hem definit al nostre codi HTML que el que farem és un POST.

Així doncs, ara hem fet un GET. L'operador or en Python funciona de la següent manera: x or y primer avalua x, si x és vertader retorna x i si no avalua y i retorna el resultat. Fixem-nos que deim que és una avaluació pererosa perquè si x dóna vertader, y mai s'arribarà a avaluar. Com que hem fet un GET la primera expressió és False i n'avaluarà la segona, amb la qual cosa obtenim un preciós None com a resultat de la inicialització de valors.

Anem ara a la segon part, la que ens defineix la instància, en aquest cas tenim un and. L'and funciona de la següent manera: x and y primer avalua x si x és fals llavors retorna el seu valor i si no s'avalua y i es retorna el seu valor. En el cas doncs de que x sigui fals ja no s'avaluarà y.

En el nostre cas hem passat el valor id=None llavors ja no itentarà ni crear una instància de l'objecte, senzillament passarà None com a valor de la instància, és a dir, res, i això indicarà a Django que es tracta d'un registre nou.

Per tant, quan hem entrat via add que que obtenim és un formulari buid. Com que hem entrat per GET l'if no s'avaluarà i Django ens presentarà una plana html amb un formulari buid.

Suposem ara que feim un /edit/2/, en aquest cas també haurem passat per GET però el valor de l'id serà 2.

formulario llavors tendrà None com a valors inicials per la mateixa raó que abans, però ara el primer terme de l'and és vertader i se n'avalua el segon, això fa que obtinguem com a valor per la instància l'objecte Person que té id igual a 2. En aquest cas tendrem un formulari ple amb els valors del registre i Django ens presentarà un formulari amb les dades de l'objecte com a valors i lligarà la instància al formulari (això significa que podrem fer formulario.save() per guardar el registre).

Molt bé, ja tenim un formulari, ple o buid, tant fa, ara el que feim és afegir o modificar dades i pitjam damunt l'opció de guardar. Això farà un post i cridarà a la mateixa url (el punt a l'action així ens ho garanteix), això significa que per a la inserció l'id serà None i per afegir l'id serà un número, 2 en el nostre cas.

Ara entram per POST, la qual cosa fa que la primera part de l'or sigui vertadera, amb la qual cosa el formulari tindrà com a valors inicials el que hagem posat, perquè això? dons per si no hem passat la validació del formulari, si anam per POST i el formulari no passa la validació, anirem novament a la mateixa plantilla, però ara tindrem com a valors del formulari el que hem passat, és a dir, si no passam la validació tornarem a tenir un formulari ple amb les dades que hem escrit.

És important fixar-se que les dades inicials a diferència de les dades que passem per la instància no necessàriament han de ser vàlides, no passen pels mecanismes de validació de Django, i això és el que ens permet recuperar tot el que hem escrit encara que estigui malament segons la validació.

Seguim, si hem entrat per edit l'id serà None i per tant ja no es crea cap objecte Person, si hem entrat per add l'id tindrà valor i s'avaluarà el segon terme de l'and amb la qual cosa haurem recuperat el registre de tipus Person que té id igual al nombre passat i l'haurem lligat al formulari.

Com que és un POST i passam la validació el que se fa és guardar el registre (si estava inicialitzat farà un update, sinó farà un insert a la base de dades i ens retornarà la instància de la classe Person que s'ha guardat.

Ara, que ja s'ha guardat sols ens queda fer un redirect de manera que evitem problemes amb dobles pitjades i refrescs varis. En el meu cas vaig a una url que em presentarà la fitxa que tot just acabam de crear/modificar.

Esper que aquest exemple us hagi agradat tant com a mi quan ho vaig veure. És tot un exemple de l'expressivitat de Python i del ben pensat que està Django. Però el més important de tot d'aquesta història és que sempre es pot aprendre alguna cosa i que per aprendre és important dedicar temps a llegir codi dels altres.

blog comments powered by Disqus