viernes, 19 de junio de 2020

Flask 2. Enrutamiento y parámetros URL

File:Url-structure.png - Wikimedia Commons


Enrutamiento y parámetros URL


Rutas Estáticas.                         



En el post anterior vimos como Flask asocia una dirección de internet, una url, con un método de nuestra aplicación. Esto lo conseguimos añadiendo el decorador "route" a la función que queremos que se ejecute cuando se llame a esa página.

Si recuerdas creamos el siguiente código dentro de nuestro programa index.py:

@app.route("/")
def hola_mundo():
    return "Hola Mundo"

Por norma, a las funciones asociadas a una URL se le denominan "Vistas".

La vista anterior lo que hace es que cuando nosotros ejecutemos nuestro programa, el servidor nos llevará a nuestra pagina principal y nos imprimirá en la pantalla del navegador un "Hola Mundo".

Bien, vamos a añadir una nueva vista a nuestro proyecto, por ejemplo, algo que suele estar en cualquier pagina web, un "acerca de". Le diremos que cuando alguien entre en nuestra página web a la dirección:

127.0.0.1:5000/acerca_de/

Le muestre un texto como "Pagina editada por Perico Perez". Y como se hace esto, muy fácil, igual que lo anterior.

@app.route("/acerca_de/"):
def acercade():
    return "Pagina editada por Perico Perez"


Esto es lo que conseguimos.




Pero, ¿Que pasa si por ejemplo nos confundimos y tecleamos 127.0.0.1:5000/acercade?

Pues que tenemos un estupendo error (404) porque no es ninguno de los nombres que hemos definido en las vistas (hasta ahora tenemos solo definimos "/" y  "/acerca_de/")


Si quieres personalizarlo y decir a quien entre, que esa página no existe utilizamos el decorador
@app.errorhandler(404)

Por ejemplo si añadimos esto:

@app.errorhandler(404)
def pagina_no_encontrada(error):
    # Aqui hay que poner un parámetro a la función porque el decorador se lo pasa.
    return 'Pues no he encontrado la página....', 404

y repetimos lo de antes, ahora habremos personalizado el error.


Nota aclaratoria sobre acabar las URLs de las vistas con el carácter ‘/’ o no hacerlo

Al definir una URL acabada con el carácter ‘/’, por ejemplo "/acerca_de/", si el usuario accede a esa URL sin dicho carácter, Flask lo redirigirá a la URL acabada en ‘/’. En cambio, si la URL se define sin acabar en ‘/’ y el usuario accede indicando la ‘/’ al final, Flask dará un error HTTP 404, con lo cual es mejor ponerla siempre.


Rutas Dinámicas.                                 


    Hasta ahora las vistas o páginas que hemos creado solo mostraban un texto, eran muy estáticas, no hacían nada más. Esto no es lo normal en un proyecto, necesitamos poder introducir parámetros o lo que viene a ser lo mismo, contenido dinámico para darles un poco de alegría y que sean más funcionales.

Antes de continuar aclarar dos cosas:

    1º) La forma de que podamos introducir un parámetro en la url será poniendo:

                                /<parametro>/    
   
 en la vista que creemos.

    2º) Por defecto todo los parámetros que metamos a través de una página web se considerarán como que fueran del tipo string, como una cadena de texto vamos, con lo que ojo si queremos introducir valores numéricos para hacer cálculos. En este caso tenemos que decirle al programa que el string nos lo convierta en un número entero con la instrucción "int"

                            /<int:parametro>/

Luego ponemos un ejemplo.


Vamos a verlo con uno o dos ejemplos sencillos. Imaginemos que hemos creado una pagina web y queremos personalizar la bienvenida en función del nombre que mandemos a la url, y ya de paso sino introducimos nada que ponga otra cosa. ¿como haríamos esto en Flask? Podríamos hacer los siguiente, escribo el código y luego lo comento. Como estamos al comienzo escribiré de nuevo todo el programa y no solo las vistas:




  • En la línea 1 importamos la biblioteca.
  • En la 2 creamos la aplicación.
  • En la 4 construimos una vista para cuando se cargue la página de inicio, ya que sino ponemos una nos saldría al ejecutar el programa un error al no localizarse la vista.
  • linea 8, con el decorador construimos una vista por si solo llamamos a la url con /hola
  • linea 9, con el decorador construimos una vista por si pasamos el nombre del usuario. /hola/pepito
  • linea 10, construimos la función bienvenida y le decimos que por defecto tendrá un parámetro llamado nombre, que tendrá por defecto el valor None (nada), pero ahí está. Con esto ¿qué conseguimos? Pues una cosa importante, si escribimos la url:
                                                                    /hola 

         y no le pasamos ningún argumento, la función no nos dará error puesto que aunque le hemos puesto que la función requiere por fuerza un argumento, este ya existe, tiene un parámetro con un valor por defecto, aunque sea ninguno. Ahora bien si la vista ponemos:

                                                                 /hola/pepito

        también funcionará ya que el parámetro por defecto se anula y el parámetro "nombre" coje el valor que le pasamos, en este caso Pepito.
  • En la linea 11 creamos una condición que viene a decir que si nombre existe, entendiendo por existir que tenga algún valor (recuerda que None es como si no existiera ya que tiene un valor nulo o cero y por tanto dará False) devuelva "Bienvenido a nuestra web" más el nombre, y en cualquier otro caso "Hola usuario anónimo".

Dos cosas. La primera en python "if nombre:" es la forma fina y corta de decir "if nombre==True:" y la segunda para mostrar el nombre he utilizado un texto con formato para insertar el valor de la variable en el texto a mostrar.

Gráficamente:

Ejecutamos la aplicación con el nombre que hayas utilizado para el ejemplo, en mi caso rutas.py con:

$ python rutas.py

y si no ponemos el parámetro veremos:


y  si lo ponemos, es decir tecleamos en la barra de direcciones "127.0.0.1:5000/hola/pepito" aparecerá:




De forma rápida construimos otro ejemplo, una vista en el que introduciendo dos números en la url nos devuelva la suma:



En la vista le decimos al programa que los números (num1 y num2) nos los pase al programa como dos números enteros para poder sumarlos y de la misma forma el resultado lo retornamos convertido en string para lo que usamos el método str de python. Si solo pones return num1 + num2 y lo ejecutas Flask te dirá básicamente que no acepta retornar un número entero por pantalla, solo texto.



Comentar que aparte de <int:loquesea> también se pueden utilizar los siguientes comandos para modificar el tipo de parámetros que introducimos en la Url:

  • string: Acepta cualquier texto sin barras (por defecto). Si no ponemos nada en el parámetro como ya dijimos lo considera como un string, una cadena de texto.
  • int: para convertirlo en enteros
  • float: para  valores reales, con decimales.
  • path: Acepta cadena de caracteres con barras

Construcción de Rutas a partir del nombre de la Función y redireciones.
    
    Para terminar, flask pone a nuestra disposición el método url_for() para crear una Url a partir del nombre de la función asociada a la vista. Es recomendable usar url_for() en vez de poner directamente la Url en el código por los siguientes motivos:
  • Si se cambia una url en cualquier momento esta variación no afectará a nuestro código. En el ejemplo anterior podríamos cambiar el decorador @app.route('/hola/') por @app.route('/hello/') y el metodo url_for('bienvenida') nos seguiría dando la url valida.
  • La función escapa los caracteres especiales, como los espacios de forma automática.
  • Las rutas generadas son siempre absolutas evitándose las rutas relativas.
Esta función acepta como parámetros el nombre de una vista y un número variable de argumentos clave-valor cada uno de ellos asociado a una parte variable de la Url. Veamos esto con un ejemplo, donde usaremos otro método muy útil, redirect(), redirigir. Este método nos lleva a la página que le pasemos como parámetro o mejor dicho la url que le proporcionamos con el método url-for. Nótese que empezamos importando los dos nuevos métodos al comienzo del código:


Si en el navegador acedemos a la dirección 127.0.0.1:5000/ir_hola/ el programa anterior nos mandará o redirigirá a la vista ' bienvenida' con el parámetro nombre= Antonio' obteniendo:


Respuestas de Error.

Si queremos en cualquier momento devolver una respuesta HTPP de error, podemos utilizar la función abort. Previamente tendremos que importarla:

from flask import abort

Imaginemos que creamos una vista llamada registro que queremos que de una respuesta de error. Podemos hacer lo siguiente:

@app.route("/registro/")
def registro():
    abort(401)
    

Y si lo ejecutamos, tenemos un error 401:
-------------------------------------------------------------------------------------------------------------------
Unauthorized

The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.
--------------------------------------------------------------------------------------------------------------------

En esta página podéis encontrar los códigos de error HTML más comunes, aunque más adelante podrás crear tu propia página de error personalizada.







miércoles, 17 de junio de 2020

Flask 1. Introducción y primer proyecto.


Introducción a Flask:


Flash es un framework de python para crear entornos web de forma sencilla. En otras palabras, para crear un sitio web que no sea estático, es decir, que no solo podamos ver su contenido sino que también podamos interactuar con el.

La documentación se puede encontrar en https://flask.palletsprojects.com/en/1.1.x/

Instalación de Flask:


    Antes de instalarlo es conveniente que lo hagamos en un Entorno Virtual, que es de forma muy simplificada un espacio o un lugar virtual donde podremos instalar python junto las bibliotecas que necesitemos sin interferir con otros proyectos. Lo que hagamos dentro de ese entorno hay se queda, instalar, borrar, ejecutar...etc. Si luego nos cansamos o necesitamos eliminar ese proyecto, borramos la carpeta y listo.

Yo estoy trabajando con Ubuntu y python 3.6. He creado una carpeta denominada Flask en el escritorio y me he metido dentro de ella. Desde la versión 3.3 de python en adelante, podemos crear un entorno virtual con el modulo venv con la instrucción:

user@ubuntu:~/Escritorio/Flask$ python3 -m venv miEntorno
                                              

Esto te creará una carpeta llamada "miEntorno" donde se instalará todo lo que el entorno virtual necesita (python, bibliotecas que instalemos etc). Una vez que lo hayamos hecho, para meternos dentro del entorno virtual, tenemos que activarlo con:

user@ubuntu:~/Escritorio/Flask$ source miEntorno/bin/activate

Y ya lo tenemos, nos aparecerá algo así como:

(miEntorno) user@ubuntu:~/Escritorio/Flask$

El (miEntorno) al principio del prompt es lo que nos indica que estamos dentro del entorno virtual.
Para salir, esta tan fácil como cerrar el terminal o ejecutar la instrucción: $ deactivate


Una vez dicho lo anterior, volvemos al asunto que era instalar Flask. Suponemos que estamos dentro de nuestro entorno virtual y ejecutamos la instrucción:

(miEntorno) $ pip3 install Flask

Con esto ya tenemos lo que necesitamos para empezar. Aunque no lo he dicho antes, si quieres también puedes instalar Flask sin necesidad de hacerlo en un entorno virtual, claro está.

Vamos a crear nuestra primera aplicación de Flask. Creamos un archivo de python y le ponemos el nombre que quieras. En mi caso le llamaré index.py y tecleamos:

from flask import Flask

app=Flask(__name__)

@app.route('/')
def hola_mundo():
    return "Hola, mundo"

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


Guardamos y ya lo tenemos. Antes de ejecutarlo vamos a explicarlo un poco.

Con "from flask import Flask" hemos importado la libreria Flask, que es lo que vamos a necesitar.

Luego hemos creado un objeto, que hemos llamado app, y que pertenece a la clase Flask. Es lo que nos permitirá posteriormente desplegar la aplicación en un servidor web y jugar con ella. 

"@app.route('/')" Es un decorador que le dirá al navegador que cuando alguien vaya a nuestra primera pagina o pagina principal o de inicio ejecute la función que vaya a continuación.

La función, que hemos llamado hola_mundo, aunque le puedes poner el nombre que quieras, simplemente retorna, o dice al navegador que muestre el texto "Hola, mundo".

Para terminar le decimos al programa que si lo ejecutamos directamente y no lo estamos importando como un modulo que ejecute la aplicación.

¡Y ya está!. Vamos a probarla a ver que pasa:

(miEntorno) $ python3 index.py

Ahora ve al navegador de tu ordenador y teclea en la barra de direcciones:

127.0.0.1:5000 
localhost:5000 

que es lo mismo.

Deberías ver, si todo ha salido bien, un Hola Mundo en la pantalla. 

Otra forma de ejecutar la aplicación es a través del propio Flask. Si el nombre de nuestra aplicación es app.py solo tenemos que ejecutar 

$ flask run

Si nuestra aplicación se llama de otra forma, entonces tenemos que usar una variable de entorno para decir a Flask cual es nuestra aplicación. Esto se hace de la siguiente forma:

$ export FLASK_APP=nombre_aplicacion
y volvemos a ejecutar flask run.

Otra tercera forma es usar la instrucción de linux "curl" que nos devolverá el código html de la página.

$ curl localhost:5000
Hola, mundo


Por defecto, nos tenemos que conectar a nuestra aplicación a través de la url 127.0.0.1:5000. Si quisiéramos cambiar el puerto o la dirección desde la que nuestro servidor web va a responder se lo tenemos que decir a la aplicación dentro de app.run(). 

Por ejemplo, si quisiéramos que nuestro servidor aceptara peticiones desde cualquier dirección a través del puerto 8080 cambiamos en el programa index.py:

app.run("0.0.0.0",8080)

y ejecutamos:

(miEntorno) user@ubuntu:~/Escritorio/Flask$ python3 index.py

El terminal nos dará la siguiente respuesta:

 * Serving Flask app "index" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)


Si vamos al navegador y ponemos cualquier Url con el puerto 8080 veríamos nuestro bonito "Hola Mundo"

Ej.  127.0.0.5:8080

Por último es recomendable cambiar el debbuger (lo que viene siendo el depurador) a True porque así  nuestra aplicación nos permitirá estudiar los posibles errores cometidos, y además se activa el modo “reload” que inicia automáticamente el servidor. Con esto podremos cambiar código en nuestro programa, guardarlo y volver a recargar el navegador y ver los resultados sin necesidad de tener que andar apagando y volviendo a encender el servidor cada vez que hagamos cambios.

Esto se consigue añadiendo a app.run() lo siguiente:

app.run(debug=True)

Ya para terminar, a mi me ocurría que al principio intentaba apagar el servidor con ctrl+z por equivocación, con lo cual estaba mandando la aplicación a un segundo plano :-{. Luego lo volvía a iniciar con python3 index.py de nuevo, y al estar trabajando en linux me salía el error:
 "OSError: [Errno 98] Address already in use". 

Vamos que no se da por enterado de que yo lo que quería era apagar el servidor (hay veces que te empeñas en dar a una tecla y erre que erre ¿no te ha pasado?) y seguía usando lógicamente el puerto 5000.  Bueno, pues si alguna vez quieres matar un proceso en uso puedes hacer lo siguiente.

$ fuser -n tcp 5000   
5000/tcp:             2097 

(lo de 5000 es, porque si no lo hemos modificado, por defecto el programa utiliza ese puerto para comunicarse). Esto nos dirá que proceso o procesos están usando ese puerto. En mi caso el puerto esta siendo usado por el proceso 2097. Pues la solución es acabar con el con:

$ kill -9 2097

Así destruimos el proceso y podemos ejecutar python3 index.py de nuevo.