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 ​​en 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 *