# Refuerzo de programación Taller de Análisis y Programación II ## Núcleos temáticos - [Instalación de Python](#Instalación-de-Python) - Windows - [Instalación de Visual Studio Code](#Instalación-de-Visual-Studio-Code) - Diferencia entre Programar y Codificar - Python - Concepto - Elementos del Lenguaje - Módulos e importación - Colecciones - Estructuras de Control de Flujo - Colecciones por comprensión - Funciones - Docstrings - Manipulación de cadenas de texto - Manipulación de diccionarios - Manipulación de listas y tuplas - Manipulación de diccionarios - Manejo y manipulación de archivos - Manejo de archivos CSV - Expresiones regulares - Creación de menús de opciones - Captura y manejo de excepciones - Generación de registros de sistema - Módulos del sistema(os, sys y subprocess) - Parelismo y concurrencia - Python CGI para aplicaciones Web - Consumo y producción de APis para arquitecturas REST/JSON - Conexiones a bases de datos con MySQL y MariaDB - Conexiones a bases de datos con Postgresql - Generación y lectura de cookies HTTP - Sistemas de control de acceso en entornos HTTPS - Programación orientada a objetos con Python - Pruebas Unitarias - [Flask](#Flask) - [¿Qué es Flask?](#¿Qué-es-Flask?) - [Instalación](#Instalación) - [Versión de Python](#Versión-de-Python) - [Dependencias](#Dependencias) - [Entornos virtuales](#Entornos-virtuales) - [Instalar Flask](#Instalar-Flask) - [Inicio rápido](#Inicio-rápido) - [Una aplicación Michimi](#Una-aplicación-Michimi) - [Modo Debug](#Modo-Debug) - [Escapando HTML](Escapando-HTML) - [Enrutador](#Enrutador) - Ficheros Estáticos - Dibujando Plantillas - Accediendo a los datos de un Request - Redirecciones y Errores - Acerca de las Responses - Sesiones - Usando Message Flashing - Logging - Utilitarios - Visual Studio Code ### Instalación de Python #### Windows Debemos ir al sitio web oficial de [Python](python.org): ![](https://hackmd.io/_uploads/r1-6ZS8gp.png) Buscar *Downloads* ![](https://hackmd.io/_uploads/B1k6fHLx6.png) Vamos a la carpeta de Descargas y ejecutamos como administrador el instalador, hay que marchar el check de `Add python.exe to PATH` y luego hacer clic sobre *Install Now*: ![](https://hackmd.io/_uploads/ry8rmrLxp.png) ![](https://hackmd.io/_uploads/HyAcXS8xa.png) ![](https://hackmd.io/_uploads/BkzJNHUeT.png) Podemos hacer clic en *Disable path lenght limit* y finalmente un clic en `Close` Comprobar si tenes Python instalado en el sistema y si este lo reconoce en la línea de comandos *cmd*: ![](https://hackmd.io/_uploads/SJv4HBLea.png) Cuando aparezca el símbolo del sistema, escribimos `python` y enter: ![](https://hackmd.io/_uploads/BJCtBBUe6.png) Saber la versión de Python que estoy usando en el sistema: ![](https://hackmd.io/_uploads/SyYyLBUeT.png) ### Instalación de Visual Studio Code #### Windows Vamos al sitio [code.visualstudio.com](https://code.visualstudio.com): ![](https://hackmd.io/_uploads/SkQtDrLeT.png) Presionamos en `Download for Windows`, : ![](https://hackmd.io/_uploads/rkFV_SLxp.png) Vamos a la carpeta de Descargas y ejecutamos el instalador(no hace falta como administrador): ![](https://hackmd.io/_uploads/r1-K_HUl6.png) Dejamos las opciones por defecto y siguiente: ![](https://hackmd.io/_uploads/H19JtBUx6.png) ![](https://hackmd.io/_uploads/rkLxYBUxT.png) ![](https://hackmd.io/_uploads/BysHYSUlp.png) Yo suelo escoger el tema oscuro y la opción `Mark Done`: ![](https://hackmd.io/_uploads/BkMptBLx6.png) ### Flask ### Requisitos del alumno - Tener una cuenta registrada en [Github](https://github.com/) para versionar el código de su proyecto - Tener una cuenta registrada en [HackMD](https://hackmd.io/) para la publicación de resultados ### ¿Qué es Flask? Flask es tecnicamente un *micro-framework*, proveyendo solamente lo necesario para tu aplicación con lo mínimo. Algo así como un extra de Python. Flask es instalable desde **Pypi**, el Python Package Index. ### Instalación #### Versión de Python Se recomienda utilizar la última versión de Python. Flask es compatible con Python 3.8 o superior. #### Dependencias Estos paquetes serán instalados automáticamente cuando se instala Flask. - [Werkzeug](https://palletsprojects.com/p/werkzeug/): implementa WSGI, la interfaz estándar de Python entre aplicaciones y servidores. - [Jinja](https://palletsprojects.com/p/jinja/): es una lenguaje de plantilla que dibuja o genera las páginas que su aplicación sirve. - [MarkupSafe](https://palletsprojects.com/p/markupsafe/): viene con Jinja. Este sirve para *escapar* entradas no confiables cuando se está generando las plantillas evitando ataques de inyección. - [ItsDangerous](https://palletsprojects.com/p/itsdangerous/): se encarga de asegurar y firmar los datos para el manejo de integridad. Es usado para proteger la *cookie* de sesión de Flask. - [Click](https://palletsprojects.com/p/click/): es un framework para escribir aplicaciones de linea de comandos. Provee comandos de flask y permite gestionar comandos personalizados. - [Blinker](https://blinker.readthedocs.io/en/stable/): provee soporte para [Signals](https://flask.palletsprojects.com/en/3.0.x/signals/). #### Entornos virtuales Es necesario utilizar un entorno virtual para *gestionar* las dependencias de nuestro proyecto, tanto en desarrollo como en producción. ¿Qué problema resuelve un entorno virtual? Cuanto más proyectos de Python tengas, es más probable que necesites trabajar con diferentes versiones de bibliotecas de Python, o incluso Python en sí mismo. Nuevas versiones de bibliotecas de un proyecto pueden romper la compatibilidad en otro proyecto. Los entornos virtuales son grupos independientes de bibliotecas de Python, **uno por cada proyecto**. Los paquetes instalados de un proyecto no afectarán a otros proyectos o paquetes del sistema operativo. Python viene incluido con el módulo [venv](https://docs.python.org/3/library/venv.html#module-venv) para crear entornos virtuales. ![](https://hackmd.io/_uploads/SkSrjIYZT.png) #### Crear un entorno virtual - Windows ``` mkdir myproject cd myproject py -3 -m venv .venv ``` - macOS/Linux ``` mkdir myproject cd myproject python3 -m venv .venv ``` #### Activar el entorno virtual - Windows `.venv\Scripts\activate` - macOS/Linux `. .venv/bin/activate` #### Instalar Flask Dentro del entorno virtual **activado**, ejecute los siguientes comandos para instalar Flask: `pip install Flask` ### Inicio rápido #### Una aplicación Michimi Una aplicación mínima de Flask se ve así: ```python from flask import Flask app = Flask(__name__) @app.route("/") def hello_world(): return "<p>Hola, Mundo!</p>" ``` ¿Qué hace ese código? 1. Primero importamos la clase **Flask**. Una instancia de esta clase será nuestra aplicación WSGI. 2. Luego creamos una instancia de esa clase. El primer argumento es el nombre de nuestra aplicación o paquete. `__name__` es un atajo apropiado en el mejor de los casos. Esto es necesario para que Flask sepa dónde buscar los recursos tales como plantillas o ficheros estáticos. 3. Vamos a usar el decorador **route** para decirle a Flask que URL deberá ser disparada en nuestra función. 4. La función retorna el mensaje que queremos mostrar en navegador web del usuario. El `content-type` por defecto es `HTML`, entonces una cadena HTML será generada por el navegador. Guardemos esto como en un fichero como un `hola.py`. No utilice nombres de aplicación como **flask.py** porque esto haría un conflicto con Flask. Para ejecutar la aplicación, use el comando `flask` o `python -m flask`. Necesitamos decirle a Flask donde está tu aplicación con la opción `--app`. En la terminal: ``` flask --app hola run ``` ![](https://hackmd.io/_uploads/r1eE4wtWp.png) Si te fijas en el tu navegador: ![](https://hackmd.io/_uploads/HJWcvwKbT.png) #### Ejercicios de fijación 1. Reproducir el tutorial, cambiar el retorno a `"<h1>Hola soy un título h1</h1>"`. 2. Crear un repositorio nuevo en Github y subir el ejercicio. 3. Socializar con los compañeros ##### Ejercicio 2 1. Ver el vídeo [Curso de Git y Github - 13 fork](https://youtu.be/9YUaf-uxuRM?si=p6_hw12jw6f82bgE) 2. Hacer un *fork* del [repositorio del profe](https://github.com/gallopeladoprofe/flask-quickstart) en tu vscode. 3. Clonar ese repositorio en su computadora 4. Crea un entorno virtual para el proyecto con lo aprendido anteriormente 5. Instala Flask dentro del entorno virtual activado 6. Probar si todo funciona correctamente en el navegador 7. Agregar dos nuevos *enpoints*: 5.1 `/titulo-h2` que muestre un título HTML de tipo h2 5.2 `/parrafo` que parrafo del tipo HTML 8. Hacer de vuelta el paso 4 9. Hacer commit y push 10. Publicar sus resultados y conclusiones en [HackMD](ttps://hackmd.io) 11. Compartir con los compañeros ##### Resultados del Ejercicio 2 ![](https://hackmd.io/_uploads/ryugZOYba.png) ![](https://hackmd.io/_uploads/SyFm-_tW6.png) #### Modo Debug El comando `flask run` puede hacer más que ejecutar el proyecto en el servidor de desarrollo. Activando el modo debug, el servidor automáticamente hará un *reload* si en el código hay cambios, y mostrará un interactivo *debugger* en el navegador si ocurre algún error durante la petición. ![](https://hackmd.io/_uploads/SkIDhdK-T.png) ##### Advertencia El debugger permite la ejecución arbitraria de código Python. Este es protegido por un PIN, pero sigue representando un riesgo mayor de seguridad. No ejecutes el servidor de desarrollo o el debugger en un entorno de producción. Para habilitar el modo debug, usar la opción `--debug`. ```python $ flask --app hola run --debug * Serving Flask app 'hola' * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: nnn-nnn-nnn ``` ##### Ejercicio 3 Aplicando los conocimientos adquiridos: Agregar a nuestro fichero `hola.py`: 1. Una lista en Python, que contenga las siguientes palabras, "administrador de sistemas, secretaria, gerente, vendedor, auxiliar de compra" 2. Un diccionario en Python, que contenga lo siguiente, id=36, nombres=Justo, apellidos=Rojas, edad=64, fechanac=1960-06-01 3. Crear un endpoint llamado `/get-lista` y retornar la lista del paso 1 4. Crear un endpoint llamado `/get-diccionario` y retornar el diccionario del paso 2 5. Probar si todo funciona correctamente en el navegador 6. Hacer commit y push 7. Publicar sus resultados y conclusiones en [HackMD](ttps://hackmd.io) 8. Compartir con los compañeros ##### Resultados del Ejercicio 3 ![](https://hackmd.io/_uploads/ryJy7KYWT.png) ![](https://hackmd.io/_uploads/BkrT7FF-a.png) #### Escapando HTML Cuando retornamos HTML(el tipo de *response* por defecto en Flask), cualquier valor proveído por el usuario en la salida *debe ser* escapado para protegernos de ataques de inyección. Las plantillas HTML son generadas con Jinja, introducidas despues, lo harán automáticamente. **escape()**, mostrada aqui, puede ser usada manualmente. Es omitida en la mayoría de ejemplos por brevedad, pero siempre deberías ser cauteloso sobre como estás usando datos no confiables. ```python from markupsafe import escape @app.route("/<ruc_cliente>") def ver_ruc_cliente(ruc_cliente): return f"El RUC es, {escape(ruc_cliente)}!" ``` ![](https://hackmd.io/_uploads/HJbZUntZT.png) Si un usuario intenta enviar el ruc_cliente algo como `<script>alert("bad")</script>`, el escaping procede a generar como texto, en vez del script en el navegador del usuario. `ruc_cliente` en la ruta se captura el valor desde la URL y pasa por la función de vista. Estas reglas son explicadas a continuación. ##### Ejercicio 4 Crea 1 endpoint: 1. `/persona-cedula/<cedula>` que retorne la persona de una lista de diccionarios(al menos tres diccionarios) 2. Probar si todo funciona correctamente en el navegador 3. Hacer commit y push 4. Publicar sus resultados y conclusiones en [HackMD](ttps://hackmd.io) 5. Compartir con los compañeros #### Enrutador Aplicaciones web modernas usan URL significativas, ¿qué quiere decir esto? cuando escribes nombres, estos deben ser autoexplicativos. Los usuarios son más de las páginas que usan este tipo de URL, ellos pueden recordarlas fácilmente y usarlas directamente. Es momento de usar el decorador **route()** para unir una función a una URL. ```python @app.route('/') def index(): return 'Index Page' @app.route('/hola') def hola(): return 'Hola, mundo' ``` ¡Puedes hacer más cosas! Puedes crear partes de URL dinámica y relacionarlas con múltiples reglas a una función. ##### Reglas en las varibales Puedes agregar secciones de variable a la URL haciendo `<nombre_de_variable>`. La función recibe `nombre_de_variable` como una palabra clave de argumento. Opcionalmente, puedes convertir a un tipo específico como `converter:nombre_de_variable`. ```python from markupsafe import escape @app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return f'User {escape(username)}' @app.route('/post/<int:post_id>') def show_post(post_id): # show the post with the given id, the id is an integer return f'Post {post_id}' @app.route('/path/<path:subpath>') def show_subpath(subpath): # show the subpath after /path/ return f'Subpath {escape(subpath)}' ``` Tipos: | | | |--------|---------------------------| | string | (Por defecto) acepta cualquier tipo sin barra | | int | acepta enteros positivos | | float | acepta positivos de coma flotante | | path | como un `string` pero acepta la barra `/` | | uuid | acepta UUID string | ##### URL únicas / Redirección Las siguientes dos reglas son diferentes en cuanto a uso de la `\`. ```python @app.route('/projects/') def projects(): return 'The project page' @app.route('/about') def about(): return 'The about page' ``` La dirección canónica URL de `projects` tiene la siguiente barra. Es similar a una carpeta del sistema de archivos. Si tenemos acceso a la URL sin la barra (`/projects`), Flask redirige para la URL canónica (`/projects/`). La dirección canónica URl de `about` no tiene la barra. Es similar a nombre de ruta para un archivo. Accediendo a la URL con la barra (`/about/`) generará un 404 "Not Found". Esto ayuda a mantener URLs únicas para estos recursos, el cual ayuda al motor de búsqueda a evitar indexar la misma página dos veces. ##### URL Building Para construir una URL en específico, se debe utilizar la función **url_for()**. Este acepta como primer argumento cualquier número de palabras claves como argumentos, cada variable corresponde a una parte de la regla de la URL. Partes de variables desconocidas son agregadas a la URL como *query parameters*. ¿Por qué necesitamos construir una URL usando una función reversiva como **url_for()** en vez de hacer a mano dentro de las plantillas? 1. La reversión es mucho más descriptiva que hacer a mano en las URLs. 2. Podemos cambiar las URLs de una en vez de recordar para agregar manualmente. 3. Construir la ULR maneja el escapado de caracteres de forma transparente. 4. La generación de *paths* son siempre absoultas, evitando comportamientos inesperados de los *paths relativos* en el navegador. 5. Si tu aplicación está expuesta fuera de la URL raiz, por ejemplo, en `/myapplication` en vez de `/`, **url_for()** gestionará apropiadamente por ti. Por ejemplo, aquí utilizamos el método **test_request_context()** para probar **url_for()**. **test_request_context()** dice a Flask como si el estuviera gestionando el request incluso si usamos la shell de Python. ```python @app.route('/') def index(): return 'index' @app.route('/login') def login(): return 'login' @app.route('/user/<username>') def profile(username): return f'{username}\'s profile' with app.test_request_context(): print(url_for('index')) print(url_for('login')) print(url_for('login', next='/')) print(url_for('profile', username='John Doe')) ``` ``` / /login /login?next=/ /user/John%20Doe ``` ##### Ejercicio 5 1. Crear un endpoint que reciba 2 parámetros y muestre su resultado al navegador 2. Crear un endpoint que reciba la edad de una persona y la verifique si es mayor de edad. ##### Métodos HTTP Las aplicaciones web usan diferentes métodos HTTP cuando acceden a sus URLs. Debemos familiarizarnos con los métodos HTTP cuando trabajamos con Flask. **Por defecto, una ruta solamente responde con peticiones GET.** Podemos utilizar *métodos de argumento* del decorador **route()** para gestionar diferentes métodos HTTP. ```python from flask import request @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': return hacer_login() else: return mostrar_formulario_login() ``` El ejemplo de abajo mantiene todos los métodos para la ruta en una función, el cual puede ser útil si tenemos partes comunes de datos. También se puede separar por vistas(views) de diferentes métodos. Flask provee de palabras clave en el decorador, por ejemplo **get(), post()** para métodos HTTP comunes. ```python @app.get('/login') def login_get(): return mostrar_formulario_login(): @app.post('/login') def login_post(): return hacer_login() ``` Si **GET** está presente, Flask automáticamente agrega y gestiona la compatibilidad con el método **HEAD**, de acuerdo con el [HTTP RFC](https://www.ietf.org/rfc/rfc2068.txt). Asimismo, **OPTIONS** se implementa automáticamente. ##### Archivos estáticos(Static Files) Las aplicaciones web dinámicas también necesitan archivos estáticos. Son los archivos CSS y Javascript. Idealmente tu servidor web está configurado para hacerlo por ti, pero durante el desarrollo con Flask, este lo puede hacer por ti también. Solo necesitamos crear una carpeta de nombre **static** en tu paquete o tu siguiente módulo y estará disponible en **/static** de la aplicación. Para generar URLs para archivos estáticos, se debe utilizar el endpoint especial `static`: ```python url_for('static', filename='style.css') ``` El archivo debe ser guardado como `static/style.css` ##### Dibujando plantillas Generar HTML con Python no es divertido, y actualmente es muy pesado porque debemos hacer el `HTML escaping` por tu cuenta para que la aplicación sea segura. Por eso Flask configura un motor de plantillas llamado [Jinja](https://palletsprojects.com/p/jinja/) para ti automáticamente. Las plantillas pueden usarse para generar cualquier tipo de texto. En aplicaciones web, primeramente se generará páginas HTML, pero se puede generar markdown, texto plano para emails, y casi cualquier cosa. Para mayor referencia sobre HTML, CSS, y otras APIs web, vaya a [MDN Web Docs](https://developer.mozilla.org/es/) Para dibujar o hacer 'render' de una plantilla, se utiliza el método **render_template()**. Todo lo que necesitar proveer es el nombre de la plantilla y las variables que deseas pasarle al motor de plantillas, como clave/valor. ```python @app.route('hello') @app.route('hello/<name>') def hello(name=None): return render_template('hello.html', name=name) ``` Flask mirará las plantillas en la carpeta **templates**. Entonces, si tu aplicación es un módulo, esta carpeta está al lado de ese módulo, si es un paquete, entonces, estará dentro del paquete: Caso 1: un módulo: ``` /app.py /templates /hello.html ``` Caso 2: un paquete: ``` /app.py /__init__.py /templates /hello.html ``` Más información a la documentación oficial [Jinja](https://jinja.palletsprojects.com/en/3.1.x/templates/) Un ejemplo usando la plantilla: ```htmlmixed <!doctype html> <title>Hello from Flask</title> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello, World!</h1> {% endif %} ``` Dentro de las plantillas también tenemos acceso a **config, request, session y g** y **url_for()** y funciones **get_flashed_messages()** Las plantillas son especialmente útiles si la herencia se utiliza. Más adelante se desarrollará el tema pero si desea mirar la web oficial [Template Inheritance](https://flask.palletsprojects.com/en/3.0.x/patterns/templateinheritance/). Básicamente la herencia de plantillas hace posible mantener ciertos elementos en cada página (como header, navigation y footer) El `escaping` es automática activado, si `name` contiene HTML, enntonces este será espacado. Si confiamos en una variable y sabemos que es segura se puede marcar como segura usando la clase **Markup** o utilizando el filtro `|safe` en la plantilla. ```python from markupsafe import Markup Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>' # Markup('<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>') Markup.escape('<blink>hacker</blink>') # Markup('&lt;blink&gt;hacker&lt;/blink&gt;') Markup('<em>Marked up</em> &raquo; HTML').striptags() 'Marked up » HTML' ```