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.