Python és un llenguatge amb una gran implantació en l'àmbit acadèmic, de recerca, en particular en intel·ligència artificial, però que també té excel·lents eines per treballar el desenvolupament web. A més, és el llenguatge que més ha prosperat en els darrers anys.
Aquest article resumeix una petita introducció ràpida a com utilitzar el microframework Flask sobre una màquina Linux Ubuntu/Debian.
Python disposa de molts frameworks i microframeworks per web. Alguns dels més coneguts:
Referències:
Aquest article també el tens en dues parts en format vídeotutorial a Youtube explicat pas a pas.
Necessitem Python 3, que ja sol venir instal·lat. Si no fos així, pots instal·lar-ho amb:
$ sudo apt install python3
Necessitem crear un entorn virtual de Python. La manera més senzilla és:
$ python -m venv env
I podem activar el nostre entorn amb:
$ source env/bin/activate
Per saber més sobre els entorns virtuals de Python visiteu l'article python_venv.
Iniciarem el nostre projecte web creant una simple carpeta:
$ mkdir pyweb1 $ cd pyweb1
Dins el nostre projecte crearem un virtualenv on posarem les nostres llibreries:
$ python -m venv env $ source env/bin/activate (env) $ pip3 install flask
Ara ja podem mirar el quickstart de la documentació de Flask. Crearem l'arxiu amb el codi super simple de Hello World a l'arxiu web.py
:
(env) $ gedit web.py &
I ara podrem arrencar la aplicació Hello World amb:
(env) $ flask --app web run
Podeu visitar la web Hello World apuntant el vostre navegador a http://localhost:5000
Si arribeu a la secció Routing entendreu com fer que la nostra aplicació web pugui tenir el què anomenem diversos punts d'entrada. Ara podem entrar a http://localhost:5000/ i per http://localhost:5000/hello
Com podeu veure a l'exemple, per crear un nou punt d'entrada (accés per una URL) hem de crear una funció (ULL! no repetir nom de funcions!!) precedida per un decorator (app.route):
#... codi previ... @app.route('/hello') def hello(): return 'Hello again!' #...mes codi...
A més, cal tenir en compte que cada URL a la que accedim, ho podem fer amb diferents mètodes. A la pràctica ho tractem com si fossin URLs independents. L'exemple típic és el d'un formulari en 2 parts:
Per accedir a les dades que se'ns envia via POST, hem d'accedir mitjançant l'objecte request de la pròpia llibreria de Flask, tal i com es pot veure en el següent exemple, utilitzant request.form['nom']
:
# si no especifiquem res al decorator, és el mètode GET @app.route('/formulari') def formulari_get(): # mostrem el formulari return """ <form method='post'> Introdueix el teu nom: <input name='nom' type='text' /> <br> <input type='submit'> </form> """ # important importar la request per accedir a les dades adjuntes from flask import request @app.route('/formulari', methods=['POST']) def formulari_post(): # processem les dades del formulari nom = request.form["nom"] return "Salut, {}".format(nom)
En un entorn web no és recomanable utilitzar variables globals. O més aviat, no podem utilitzar-les com solem fer-ho en aplicacions d'escriptori. Les aplicacions web s'executen en algun servidor web (Apache, Nginx) o servidor d'aplicacions (uwsgi, gunicorn, Heroku).
De fet, sí que es poden fer servir, però no es pot confiar en què en la següent execució la variable global es mantingui. El servidor web, depenent de la seva càrrega, pot destruir el procés d'execució de la teva aplicació web, i quan tornes a sol·licitar la web, es torna a posar en marxa tot des de zero (i, per tant, destruint les variables globals).
A més, també hi ha una qüestió de concurrència: el servidor web pot disposar de diferents threads o fils d'execució per tal de paral·lelitzar les sol·licituds en diferents CPUs (amb el què aconseguim més capacitat de processament). Si féssim servir variables globals, la informació en diferents instants ens donaria resultats incoherents.
Conclusió: si volem persistència en un entorn web, estem obligats a utilitzar fitxers o bases de dades.
Necessitem un mecanisme més sofisticat per crear contingut HTML i que no sigui fent un return d'un string. Una de les bones pràctiques més transversals a la majoria de frameworks és separar nítidament el codi de processament de la renderització HTML. Per tant, no és una bona pràctica posar contingut HTML dins l'arxiu .py en el què estem treballant.
Per això, separarem les pàgines HTML en les plantilles o templates i que renderitzarem des del codi. Per veure un exemple i un resum de les possibilitats que ens ofereixen les plantilles Jinja2, podeu mirar-vos aquestes fonts:
Com a exemple d'ús de les plantilles agafarem un carro de la compra d'un e-commerce. No serà del tot funcional, ja que ens falta el mecanisme de les sessions per poder gestionar la concurrència al servidor, i distingir entre els diferents clients.
Farem 2 rutes:
Crea una aplicació mínima com la de l'exemple i afegeix aquestes dues rutes:
@app.route('/compra') def compra_get(): return render_template('compra_form.html') @app.route('/compra',methods=["POST"]) def compra_post(): return render_template('compra_post.html')
Com podeu veure, ara ja no creem el HTML dins el codi de l'arxiu '.py' sinó que el posarem en arxius HTML que cal que estiguin a la carpeta /templates
. Aquests son els arxius compra_form.html i compra_post.html
<form method="post"> <select name="producte"> <option>Peres</option> <option>Pomes</option> <option>Prunes</option> </select> <input type="number" step="0.1" name="quantitat"> <br> <input type="submit"> </form>
<p>Gràcies per la compra!</p>
Per millorar l'exemple primer de tot personalitzarem el missatge POST on recollim el producte comprat. El què voldrem és dir-li a l'usuari quin producte i quantitat han estat afegits al carro:
from flask import request @app.route('/compra',methods=["POST"]) def compra_post(): prod = request.form['producte'] quant = request.form['quantitat'] return render_template('compra_post.html', producte=prod, quantitat=quant)
<h2>Gràcies per la teva compra!</h2> <p>Has comprat {{quantitat}} kg de {{producte}}</p>
Ara pots provar l'aplicació i veuràs que les dades que s'envien del formulari es mostren a la 2a pàgina. Observa que per passar les dades el què fem és passar-les com a keyworded arguments al render_template
, i els renderitzem amb {{}}
Pot semblar una contradicció que hi hagi codi dins d'una plantilla, però en realitat és necessari per renderitzar amb facilitat. No és codi de processament, és codi relacionat amb la renderització. Pel nostre cas, es tracta de mostrar els productes que tenim disponibles de forma dinàmica.
Per seguir, convé fer-li un cop d'ull a la documentació de les plantilles Jinja2. Observa l'exemple que surt: en poques línies ens demostren la majoria de funcions que ens cal per a les plantilles.
Pel nostre exemple de la botiga, farem una simulació de que disposem d'una llista de productes (en una variable global productes
) i l'enviarem a la plantilla per tal que sigui renderitzada en el select
del formulari:
productes = ['Cogombres','Pomes','Kiwis'] @app.route('/compra') def compra_get(): return render_template('compra_form.html', prods=productes)
I renderitzarem la llista de productes en les diferents options
dins la plantilla compra_form.html
:
<form method="post"> <select name="producte"> {% for prod in prods %} <option>{{prod}}</option> {% endfor %} </select> <input type="number" step="0.1" name="quantitat"> <br> <input type="submit"> </form>
Comprova que, en efecte funciona la plantilla, canviant la llista de productes i fent-la més llarga.
Les plantilles Jinja2 ofereixen moltes possibilitats tal i com s'explica a la doc oficial de Jinja Templates. Destaco els 2 recursos més habituals d'utilitzar, sobretot quan es tracta de tenir una mateixa imatge corporativa en totes les pàgines de l'aplicació:
blocks
o seccions, i després derivar les pàgines filles (on podem definir o sobreescriure cada block
).
Hem fet un repàs de les funcions bàsiques del framework Flask. Ens queda un parell de temes per cobrir tots els temes essencials, i que podeu seguir investigant en el propi quickstart de Flask:
També hi ha aquest cursos que poden ser interessants:
Pàgina de login de l'app:
Aquest article continua a Python Web Test i a Docker Flask.