bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


Barra lateral

ASIX Administració de Sistemes Informàtics i Xarxes
Tots els mòduls del cicle
MP01 Implantació de sistemes operatius
Totes les UFs del modul
MP02 Gestió de bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Fonaments de maquinari
Totes les UFs del modul
MP06 Administració de sistemes operatius
Totes les UFs del modul
MP07 Planificació i administració de xarxes
Totes les UFs del modul
MP08 Serveis de xarxa i Internet
Totes les UFs del modul
MP09 Implantació d'aplicacions web
Totes les UFs del modul
MP10 Administració de sistemes gestors de bases de dades
Totes les UFs del modul
MP11 Seguretat i alta disponibilitat
Totes les UFs del modul
MP12 Formació i orientació laboral
Totes les UFs del modul
MP13 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP14 Projecte
Totes les UFs del modul
DAM Desenvolupament d’aplicacions multiplataforma
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Accés a dades
Totes les UFs del modul
MP07 Desenvolupament d’interfícies
Totes les UFs del modul
MP08 Programació multimèdia i dispositius mòbils
Totes les UFs del modul
MP09 Programació de serveis i processos
Totes les UFs del modul
MP10 Sistemes de gestió empresarial
Totes les UFs del modul
MP11 Formació i orientació laboral
Totes les UFs del modul
MP12 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP13 Projecte de síntesi
Totes les UFs del modul
MPDual Mòdul Dual / Projecte
DAW Desenvolupament d’aplicacions web
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació
Totes les UFs del modul
MP04 Llenguatge de marques i sistemes de gestió d’informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Desenvolupament web en entorn client
Totes les UFs del modul
MP07 Desenvolupament web en entorn servidor
Totes les UFs del modul
MP08 Desplegament d'aplicacions web
Totes les UFs del modul
MP09 Disseny d'interfícies web
Totes les UFs del modul
MP10 Formació i Orientació Laboral
Totes les UFs del modul
MP11 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP12 Projecte de síntesi
Totes les UFs del modul
SMX Sistemes Microinformàtics i Xarxes
Tots els mòduls del cicle
MP01 Muntatge i manteniment d’equips
Totes les UFs del modul
MP02 Sistemes Operatius Monolloc
Totes les UFs del modul
MP03 Aplicacions ofimàtiques
Totes les UFs del modul
MP04 Sistemes operatius en xarxa
Totes les UFs del modul
MP05 Xarxes locals
Totes les UFs del modul
MP06 Seguretat informàtica
Totes les UFs del modul
MP07 Serveis de xarxa
Totes les UFs del modul
MP08 Aplicacions Web
Totes les UFs del modul
MP09 Formació i Orientació Laboral
Totes les UFs del modul
MP10 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP11 Anglès
Totes les UFs del modul
MP12 Síntesi
Totes les UFs del modul
CETI Ciberseguretat en Entorns de les Tecnologies de la Informació
Tots els mòduls del cicle
python_web

Python Web amb Flask

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:

  1. Flask : essencial, però amb tot el necessari i ben mantingut
  2. Django : the framework for perfectionists with deadlines , permet el desenvolupament molt ràpid, amb eines com el ORM i un admin panel molt potent.
  3. CherryPy : microframework
  4. Bottle : microframework

Aquest article també el tens en dues parts en format vídeotutorial a Youtube explicat pas a pas.

Instal·lació

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.


Creant el primer projecte

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

Per simplificar una mica, podem afegir al codi inicial dues línies al final de l'arxiu:

if __name__ == "__main__":
    app.run()

I ara podrem arrencar la aplicació Hello World amb:

  (env) $ python3 web.py

Podeu visitar la web Hello World apuntant el vostre navegador a http://localhost:5000

Avançant amb Flask

Aplicació mínima

Com podeu veure al quickstart de Flask, en molt poques línies (5 en concret) es pot crear una aplicació base.

Li hem afegit una línia més per arrencar l'aplicació amb més facilitat. La transcric aquí per més comoditat:

web.py
from flask import Flask
app = Flask(__name__)
 
@app.route('/')
def hello_world():
    return 'Hello, World!'
 
# arranquem l'aplicació
if __name__ == "__main__":
    app.run(debug=True)

Arrencant amb debug=True tindrem missatges d'error més clars i autoreload quan apliquem canvis a l'arxiu. Podem arrencar-la amb:

(env) $ python web.py

I mirar el resultat apuntant el browser a:

http://127.0.0.1:5000/

Routing

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):

web.py
@app.route('/hello')
def hello():
    return 'Hello again!'

Mètodes [GET,POST,+]

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:

  • GET: és el mètode per defecte, i sol mostrar un formulari estàtic
  • POST: un cop omplert el formulari, processem les dades

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']:

web.py
# 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)

Exercici

  • Crea un formulari que demani usuari i contrasenya. El formulari s'accedeix per mètode GET però envia les dades per mètode POST.
  • Crea una pàgina (per POST) que respongui al formulari d'usuari i contrasenya. Crea un diccionari global amb noms d'usuari i contrasenyes vàlids (hardcoded).
  • Assegura't que la pàgina de resposta al formulari respon amb un missatge que contingui un «OK» a la pàgina, i si el nom d'usuari és erroni respon adequadament amb un missatge que contingui «ERROR».

Variables globals (shhht! q no ho senti ningú!)

En un entorn web no és recomanable utilitzar variables globals. O més aviat, no podem utilitzar-les com solem fer-ho en les aplicacions corrents. 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.


Utilitzant plantilles (templates)

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:

Exemple : carro de compra

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:

  • /compra GET : ens mostrarà el formulari.
  • /compra POST : processarem les dades enviades al formulari.

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

compra_form.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>
compra_post.html
<p>Gràcies per la compra!</p>

Renderitzar dades a una plantilla amb {{ ... }}

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)

compra_post.html
<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 {{}}

Utilitzar codi dins de les plantilles 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:

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.

Plantilles like a PRO

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ó:

  • Includes és una senzilla manera d'afegir capçalera i footers iguals a totes les nostres pàgines. Us recomano utilitzar això per començar.
  • Herència de plantilles és un mètode una mica més sofisticat amb alguns avantatges, però costa una mica més d'entendre. Cal definir una pàgina master dividida en blocks o seccions, i després derivar les pàgines filles (on podem definir o sobreescriure cada block).


Testing

Anem a veure com podem testejar les nostres aplicacions fetes amb Flask.

Fem-li un cop d'ull a la documentació de test amb Flask.

Primera cosa que cal fer, instal·lar el mòdul de tests pytest:

(env) $ pip install pytest

Després, ens diuen que fem un test skeleton, però l'exemple que ens donen té una configuració avançada per posar a punt la base de dades, utilitzant una app factory, etc.

Nosaltres implementarem una versió simplificada sobre l'arxiu test_web.py:

test_web.py
import pytest
 
from web import app
 
@pytest.fixture
def client():
    with app.test_client() as client:
        yield client
 
def test_landing_page(client):
	rv = client.get('/')
	assert b'Hello, World' in rv.data

Correm els tests amb la comanda pytest:

(env) $ pytest
============================ test session starts =============================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /home/enric/dev21/basicflask
collected 1 item                                                             

test_web.py .                                                          [100%]

============================= 1 passed in 0.09s ==============================

Com podem veure, pytest posa en marxa els tests dels arxius que comencen per test_, i dins d'aquests, corre les funcions que també comencin per test_.

Exercici

Examina la secció "logging in and out" de la documentació dels tests i realitza el següent exercici:

  • Crea un formulari que demani usuari i contrasenya. El formulari s'accedeix per mètode GET però envia les dades per mètode POST.
  • Crea una pàgina (per POST) que respongui al formulari d'usuari i contrasenya. Crea un diccionari global amb noms d'usuari i contrasenyes vàlids (hardcoded).
  • Assegura't que la pàgina de resposta al formulari respon amb un missatge que contingui un «OK» a la pàgina, i si el nom d'usuari és erroni respon adequadament amb un missatge que contingui «ERROR».
  • Comprova manualment el punt anterior. Abans de fer els tests automàtics cal fer-los manuals PER A TOTS ELS CASOS (tant si funciona OK, com si dona error).
  • Afegeix dues funcions de test que comprovin el següent:
    1. Que els usuaris i contrasenyes que has hardcodejat al codi es loguen correctament.
    2. Que una combinació usuari/contrasenya que no ha estat introduïda no funciona i dona el missatge d'error.


Per a saber-ne més

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:

  • Persistència en base de dades
  • Sessions

També hi ha aquest cursos que poden ser interessants:


python_web.txt · Darrera modificació: 2021/10/18 10:07 per enrique_mieza_sanchez