Construyendo una aplicaci贸n Todo con Flask en Python

    Introducci贸n

    En este tutorial, vamos a crear una API, o un servicio web, para una aplicaci贸n de tareas pendientes. El servicio API se implementar谩 mediante una arquitectura basada en REST.

    Nuestra aplicaci贸n tendr谩 las siguientes caracter铆sticas principales:

    • Crea un art铆culo en la lista de tareas pendientes
    • Lea la lista completa de tareas pendientes
    • Actualice los elementos con el estado “No iniciado”, “En curso” o “Completado”
    • Eliminar los elementos de la lista

    驴Qu茅 es REST?

    DESCANSO, o REpresentational State Transfer, es un estilo arquitect贸nico para crear servicios web y API. Requiere que los sistemas que implementan REST sean ap谩tridas. El cliente env铆a una solicitud al servidor para recuperar o modificar recursos sin saber en qu茅 estado se encuentra el servidor. Los servidores env铆an la respuesta al cliente sin necesidad de saber cu谩l fue la comunicaci贸n anterior con el cliente.

    Cada solicitud al sistema RESTful com煤nmente usa estos 4 verbos HTTP:

    • OBTENER: Obtenga un recurso espec铆fico o una colecci贸n de recursos
    • ENVIAR: Crea un nuevo recurso
    • PONER: Actualiza un recurso espec铆fico
    • ELIMINAR: Eliminar un recurso espec铆fico

    Aunque otros est谩n permitidos y a veces se usan, como PARCHE, CABEZAy OPCIONES.

    驴Qu茅 es Flask?

    Matraz es un marco para Python para desarrollar aplicaciones web. No tiene opiniones, lo que significa que no toma decisiones por usted. Debido a esto, no se limita a estructurar su aplicaci贸n de una manera particular. Proporciona mayor flexibilidad y control a los desarrolladores que lo utilizan. Flask le proporciona las herramientas b谩sicas para crear una aplicaci贸n web y se puede ampliar f谩cilmente para incluir la mayor铆a de las cosas que necesitar铆a incluir en su aplicaci贸n.

    Algunos otros marcos web populares pueden considerarse una alternativa a Flask. Django es una de las alternativas m谩s populares si Flask no funciona para usted. Hemos hecho una comparaci贸n entre Django y Flask en este tutorial.

    Configuraci贸n de Flask

    Primero, sigamos e instalemos Flask usando pip:

    $ pip install Flask
    

    Configuremos r谩pidamente Flask y activemos un servidor web en nuestra m谩quina local. Crea un archivo main.py en el todo_service_flask directorio:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("https://Pharos.sh.com/")
    def hello_world():
        return 'Hello World!'
    

    Despu茅s de importar Flask, configuramos una ruta. Una ruta se especifica mediante un patr贸n de URL, un m茅todo HTTP y una funci贸n que recibe y gestiona una solicitud HTTP. Hemos vinculado esa ruta con una funci贸n de Python que se invocar谩 cada vez que se solicite esa URL a trav茅s de HTTP. En este caso, hemos configurado la ruta ra铆z (/) para que se pueda acceder a ella mediante el patr贸n de URL http://[IP-OR-DOMAIN]:[PORT]/.

    Ejecutando la aplicaci贸n Flask

    El siguiente trabajo es poner en marcha un servidor local y servir este servicio web para que podamos acceder a 茅l a trav茅s de un cliente.

    Afortunadamente, todo esto se puede hacer con un solo comando simple:

    $ FLASK_APP=main.py flask run
    

    Deber铆a ver el mensaje en la consola:

    Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    

    Nosotros podemos usar rizo para disparar una solicitud GET. Si est谩 en Mac, cURL ya deber铆a estar instalado en su sistema:

    $ curl -X GET http://127.0.0.1:5000/
    

    Deber铆amos ser recibidos con la respuesta:

    Hello World!
    

    La historia no termina aqu铆. Sigamos adelante y estructuramos nuestra aplicaci贸n Todo.

    Estructuraci贸n de la aplicaci贸n Todo

    Nuestra aplicaci贸n Todo tendr谩 varias caracter铆sticas fundamentales:

    • Agregar elementos a una lista
    • Obtener todos los elementos de la lista
    • Actualizar un elemento de la lista
    • Eliminar un elemento de la lista

    Estos a menudo se conocen como CRUD operaciones, para crear, leer, actualizar y eliminar.

    Usaremos la base de datos SQLite para almacenar datos, que es una base de datos basada en archivos muy ligera. Puede instalar el Navegador de bases de datos para SQLite para crear f谩cilmente una base de datos.

    Vamos a nombrar esta base de datos todo.db y col贸quelo debajo del directorio todo_service_flask. Ahora, para crear una tabla, ejecutamos una consulta simple:

    CREATE TABLE "items" (
        "item" TEXT NOT NULL,
        "status" TEXT NOT NULL,
        PRIMARY KEY("item")
    );
    

    Adem谩s, para simplificar las cosas, escribiremos todas nuestras rutas en un solo archivo, aunque esto no siempre es una buena pr谩ctica, especialmente para aplicaciones muy grandes.

    Tambi茅n usaremos un archivo m谩s para contener nuestras funciones auxiliares. Estas funciones tendr谩n la l贸gica empresarial para procesar la solicitud conect谩ndose a la base de datos y ejecutando las consultas correspondientes.

    Una vez que se sienta c贸modo con esta estructura inicial de Flask, puede reestructurar su aplicaci贸n de la forma que desee.

    Construyendo la aplicaci贸n

    Para evitar escribir l贸gica varias veces para tareas que se ejecutan com煤nmente, como agregar elementos a una base de datos, podemos definir funciones auxiliares en un archivo separado y simplemente llamarlas cuando sea necesario. Para este tutorial nombraremos el archivo helper.py.

    Agregar elementos

    Para implementar esta caracter铆stica, necesitamos dos cosas:

    • Una funci贸n auxiliar que contiene l贸gica empresarial para agregar un nuevo elemento en la base de datos
    • Una ruta que se debe llamar cada vez que se alcanza un punto final HTTP en particular

    Primero, definamos algunas constantes y escribamos el add_to_list() funci贸n:

    import sqlite3
    
    DB_PATH = './todo.db'   # Update this path accordingly
    NOTSTARTED = 'Not Started'
    INPROGRESS = 'In Progress'
    COMPLETED = 'Completed'
    
    def add_to_list(item):
        try:
            conn = sqlite3.connect(DB_PATH)
    
            # Once a connection has been established, we use the cursor
            # object to execute queries
            c = conn.cursor()
    
            # Keep the initial status as Not Started
            c.execute('insert into items(item, status) values(?,?)', (item, NOTSTARTED))
    
            # We commit to save the change
            conn.commit()
            return {"item": item, "status": NOTSTARTED}
        except Exception as e:
            print('Error: ', e)
            return None
    

    Esta funci贸n establece una conexi贸n con la base de datos y ejecuta una consulta de inserci贸n. Devuelve el elemento insertado y su estado.

    A continuaci贸n, importaremos algunos m贸dulos y configuraremos una ruta para la ruta. /item/new:

    import helper
    from flask import Flask, request, Response
    import json
    
    app = Flask(__name__)
    
    @app.route("https://Pharos.sh.com/")
    def hello_world():
        return 'Hello World!'
    
    @app.route('/item/new', methods=['POST'])
    def add_item():
        # Get item from the POST body
        req_data = request.get_json()
        item = req_data['item']
    
        # Add item to the list
        res_data = helper.add_to_list(item)
    
        # Return error if item not added
        if res_data is None:
            response = Response("{'error': 'Item not added - " + item + "'}", status=400 , mimetype="application/json")
            return response
    
        # Return response
        response = Response(json.dumps(res_data), mimetype="application/json")
    
    return response
    

    los request El m贸dulo se utiliza para analizar la solicitud y obtener datos del cuerpo HTTP o los par谩metros de consulta de la URL. response se utiliza para devolver una respuesta al cliente. La respuesta es de tipo JSON.

    Si desea leer m谩s sobre lectura y escritura de JSON en Python, 隆lo tenemos cubierto!

    Devolvimos un estado de 400 si el art铆culo no se agreg贸 debido a alg煤n error del cliente. los json.dumps() La funci贸n convierte el diccionario o el objeto de Python en un objeto JSON v谩lido.

    Guardemos el c贸digo y verifiquemos si nuestra funci贸n est谩 implementada correctamente.

    Podemos usar cURL para enviar una solicitud POST y probar nuestra aplicaci贸n. Tambi茅n necesitamos pasar el nombre del elemento como cuerpo POST:

    $ curl -X POST http://127.0.0.1:5000/item -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json'
    

    Si est谩 en Windows, deber谩 formatear los datos JSON de comillas simples a comillas dobles y escapar:

    $ curl -X POST http://127.0.0.1:5000/item -d "{"item": "Setting up Flask"}" -H 'Content-Type: application/json'
    

    Tenga en cuenta lo siguiente:

    • Nuestra URL consta de dos partes: una URL base (http://127.0.0.1:5000) y la ruta o camino (/item/new)
    • El m茅todo de solicitud es POST
    • Una vez que la solicitud llega al servidor web, intenta localizar el punto final bas谩ndose en esta informaci贸n
    • Estamos pasando los datos en formato JSON – {“item”: “Configurando Flask”}

    A medida que disparamos la solicitud, deber铆amos recibir la respuesta:

    {"Setting up Flask": "Not Started"}
    

    Ejecutemos el siguiente comando para agregar un elemento m谩s a la lista:

    $ curl -X POST http://127.0.0.1:5000/item -d '{"item": "Implement POST endpoint"}' -H 'Content-Type: application/json'
    

    Deber铆amos ser recibidos con la respuesta, que nos muestra la descripci贸n de la tarea y su estado:

    {"Implement POST endpoint": "Not Started"}
    

    隆隆隆Felicidades!!! Hemos implementado con 茅xito la funcionalidad para agregar un art铆culo a la lista de tareas pendientes.

    Recuperando todos los elementos

    A menudo deseamos obtener todos los elementos de una lista, lo que afortunadamente es muy f谩cil:

    def get_all_items():
        try:
            conn = sqlite3.connect(DB_PATH)
            c = conn.cursor()
            c.execute('select * from items')
            rows = c.fetchall()
            return { "count": len(rows), "items": rows }
        except Exception as e:
            print('Error: ', e)
            return None
    

    Esta funci贸n establece una conexi贸n con la base de datos y crea una consulta SELECT y luego la ejecuta a trav茅s de c.fetchall(). Esto devuelve todos los registros devueltos por la consulta SELECT. Si estamos interesados 鈥嬧媏n un solo art铆culo, podemos llamar c.fetchone().

    Nuestro m茅todo get_all_items devuelve un objeto Python que contiene 2 elementos:

    • La cantidad de elementos devueltos por esta consulta
    • Los elementos reales devueltos por la consulta

    En main.py, definiremos una ruta /item/new que acepta una solicitud GET. Aqu铆 no pasaremos el methods argumento de palabra clave para @app.route(), porque si omitimos este par谩metro, entonces est谩 predeterminado en GET:

    @app.route('/items/all')
    def get_all_items():
        # Get items from the helper
        res_data = helper.get_all_items()
    
        # Return response
        response = Response(json.dumps(res_data), mimetype="application/json")
        return response
    

    Usemos cURL para buscar los elementos y probar nuestra ruta:

    $ curl -X GET http://127.0.0.1:5000/items/all
    

    Deber铆amos recibir la respuesta:

    json {"count": 2, "items": [["Setting up Flask", "Not Started"], [Implement POST endpoint", "Not Started"]]}

    Obtener el estado de elementos individuales

    Como hicimos con el ejemplo anterior, escribiremos una funci贸n auxiliar para esto:

    def get_item(item):
    try:
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        c.execute("select status from items where item='%s'" % item)
        status = c.fetchone()[0]
        return status
    except Exception as e:
        print('Error: ', e)
        return None
    

    Tambi茅n definiremos una ruta en main.py para analizar la solicitud y entregar la respuesta. Necesitamos la ruta para aceptar una solicitud GET y el nombre del elemento debe enviarse como par谩metro de consulta.

    Se pasa un par谩metro de consulta con el formato ?name=value con la URL. p.ej http://base-url/path/to/resource/?name=value. Si hay espacios en el valor, debe reemplazarlos con + o con %20, que es la versi贸n codificada en URL de un espacio. Puede tener varios pares de nombre-valor separ谩ndolos con el & personaje.

    Estos son algunos de los ejemplos v谩lidos de par谩metros de consulta:

    • http://127.0.0.1:8080/search?query=what+is+flask
    • http://127.0.0.1:8080/search?category=mobiles&brand=apple
    @app.route('/item/status', methods=['GET'])
    def get_item():
        # Get parameter from the URL
        item_name = request.args.get('name')
    
        # Get items from the helper
        status = helper.get_item(item_name)
    
        # Return 404 if item not found
        if status is None:
            response = Response("{'error': 'Item Not Found - %s'}"  % item_name, status=404 , mimetype="application/json")
            return response
    
        # Return status
        res_data = {
            'status': status
        }
    
        response = Response(json.dumps(res_data), status=200, mimetype="application/json")
        return response
    

    Nuevamente, usemos cURL para disparar la solicitud:

    $ curl -X GET http://127.0.0.1:5000/item/status?name=Setting+up+Flask
    

    Deber铆amos recibir la respuesta:

    {"status": "Not Started"}
    

    Actualizar elementos

    Dado que hemos completado la tarea “Configuraci贸n de Flask” hace un tiempo, es hora de que actualicemos su estado a “Completado”.

    Primero, escribamos una funci贸n en helper.py que ejecuta la consulta de actualizaci贸n:

    def update_status(item, status):
        # Check if the passed status is a valid value
        if (status.lower().strip() == 'not started'):
            status = NOTSTARTED
        elif (status.lower().strip() == 'in progress'):
            status = INPROGRESS
        elif (status.lower().strip() == 'completed'):
            status = COMPLETED
        else:
            print("Invalid Status: " + status)
            return None
    
        try:
            conn = sqlite3.connect(DB_PATH)
            c = conn.cursor()
            c.execute('update items set status=? where item=?', (status, item))
            conn.commit()
            return {item: status}
        except Exception as e:
            print('Error: ', e)
            return None
    

    Es una buena pr谩ctica no depender de la entrada del usuario y hacer nuestras validaciones, ya que nunca sabemos qu茅 podr铆a hacer el usuario final con nuestra aplicaci贸n. Aqu铆 se realizan validaciones muy simples, pero si se tratara de una aplicaci贸n del mundo real, querr铆amos protegernos contra otras entradas maliciosas, como inyecci贸n SQL Ataques.

    A continuaci贸n, configuraremos una ruta en main.py que acepta un m茅todo PUT para actualizar el recurso:

    @app.route('/item/update', methods=['PUT'])
    def update_status():
        # Get item from the POST body
        req_data = request.get_json()
        item = req_data['item']
        status = req_data['status']
    
        # Update item in the list
        res_data = helper.update_status(item, status)
    
        # Return error if the status could not be updated
        if res_data is None:
            response = Response("{'error': 'Error updating item - '" + item + ", " + status   +  "}", status=400 , mimetype="application/json")
            return response
    
        # Return response
        response = Response(json.dumps(res_data), mimetype="application/json")
    
        return response
    

    Usemos cURL para probar esta ruta, como antes:

    $ curl -X PUT http://127.0.0.1:5000/item/update -d '{"item": "Setting up Flask", "status": "Completed"}' -H 'Content-Type: application/json'
    

    Deber铆amos ser recibidos con la respuesta:

    {"Setting up Flask": "Completed"}
    

    Eliminar elementos

    Primero, escribiremos una funci贸n en helper.py que ejecuta la consulta de eliminaci贸n:

    def delete_item(item):
        try:
            conn = sqlite3.connect(DB_PATH)
            c = conn.cursor()
            c.execute('delete from items where item=?', (item,))
            conn.commit()
            return {'item': item}
        except Exception as e:
            print('Error: ', e)
            return None
    

    Nota: Tenga en cuenta que (item,) no es un error tipogr谩fico. Tenemos que pasar execute() una tupla incluso si solo hay un elemento en la tupla. Agregar la coma obliga a que se convierta en una tupla.

    A continuaci贸n, configuraremos una ruta en main.py que acepta la solicitud DELETE:

    @app.route('/item/remove', methods=['DELETE'])
    def delete_item():
        # Get item from the POST body
        req_data = request.get_json()
        item = req_data['item']
    
        # Delete item from the list
        res_data = helper.delete_item(item)
    
        # Return error if the item could not be deleted
        if res_data is None:
            response = Response("{'error': 'Error deleting item - '" + item +  "}", status=400 , mimetype="application/json")
            return response
    
        # Return response
        response = Response(json.dumps(res_data), mimetype="application/json")
    
        return response
    

    Usemos cURL para probar nuestra ruta de eliminaci贸n:

    $ curl -X DELETE http://127.0.0.1:5000/item/remove -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json'
    

    Deber铆amos ser recibidos con la respuesta:

    {"item": "Temporary item to be deleted"}
    

    隆Y eso redondea la aplicaci贸n con todas las funciones de back-end que necesitamos!

    Conclusi贸n

    Espero que este tutorial le haya dado una buena comprensi贸n de c贸mo usar Flask para construir una aplicaci贸n web simple basada en REST. Si tiene experiencia con otros marcos de Python como Django, es posible que haya observado que es mucho m谩s f谩cil usar Flask.

    Este tutorial se centr贸 m谩s en el aspecto back-end de la aplicaci贸n, sin ninguna GUI, aunque tambi茅n puede usar Flask para renderizar p谩ginas HTML y plantillas, que guardaremos para otro art铆culo.

    Si bien est谩 perfectamente bien usar Flask para administrar plantillas HTML, la mayor铆a de las personas usan Flask para crear servicios de backend y crear la parte de frontend de la aplicaci贸n utilizando cualquiera de las bibliotecas populares de JavaScript. Puede probar lo que mejor le funcione. 隆Buena suerte en tu viaje con Flask!

    Si desea jugar con el c贸digo fuente o tiene alguna dificultad para ejecutarlo desde el c贸digo anterior, aqu铆 est谩 en GitHub!

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *