Construyendo una aplicación de Todo con Flask en Python

Introducción

En este tutorial, vamos a construir una API, o un servicio web, para una aplicación de todo. El servicio API se implementará utilizando una arquitectura basada en REST.
Nuestra aplicación tendrá las siguientes características principales:
  • Crear un elemento en la lista de tareas pendientes.
  • Lee la lista completa de tareas
  • Actualice los elementos con el estado como "No iniciado", "En curso" o "Completo"
  • Eliminar los elementos de la lista

¿Qué es REST?

REST , o Representación estatal de transferencia , 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:
  • GET : Obtén un recurso específico o una colección de recursos
  • POST : Crear un nuevo recurso
  • PUT : Actualizar un recurso específico
  • ELIMINAR : Eliminar un recurso específico
Aunque otros están permitidos y, a veces, se utilizan, como PATCH , HEAD y OPTIONS .

¿Qué es el matraz?

Flask es un framework para Python para desarrollar aplicaciones web. No tiene opiniones, lo que significa que no toma decisiones por usted. Debido a esto, no restringe la estructura de 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 extender fácilmente para incluir la mayoría de las cosas que necesitaría incluir en su aplicación.
Algunos otros marcos web populares pueden ser considerados como 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 matraz

Primero, sigamos adelante 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.pyen el todo_service_flaskdirectorio:
from flask import Flask  
app = Flask(__name__)

@app.route('/')
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 maneja una solicitud HTTP. Hemos enlazado esa ruta con una función de Python que se invocará cada vez que se solicite la URL a través de HTTP. En este caso, hemos configurado la ruta raíz (/) para que se pueda acceder mediante el patrón de URL http://[IP-OR-DOMAIN]:[PORT]/.

Ejecutando la aplicación Frask

El siguiente trabajo es activar 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ías ver el mensaje en la consola:
Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)  
Podemos usar cURL 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/
Debemos ser recibidos con la respuesta:
Hello World!  
La historia no termina aquí. Sigamos adelante y estructuremos nuestra aplicación Todo.

Estructurando 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
  • Actualizando un artículo en la lista
  • Eliminar un elemento de la lista
A menudo se hace referencia a ellas como operaciones CRUD , para crear, leer, actualizar y eliminar .
Usaremos la base de datos SQLite para almacenar datos, que es una base de datos muy liviana basada en archivos. Puede instalar el DB Browser for SQLite para crear fácilmente una base de datos.
Nombremos esta base de datos todo.dby la coloquemos en el directorio todo_service_flaskAhora, 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 mantener las cosas simples, 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 de ayuda. Estas funciones tendrán la lógica empresarial para procesar la solicitud conectándose a la base de datos y ejecutando las consultas apropiadas.
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 la lógica varias veces para tareas que se ejecutan comúnmente, como agregar elementos a una base de datos, podemos definir las funciones de ayuda en un archivo separado y simplemente llamarlos cuando sea necesario. Para este tutorial nombraremos el archivo helper.py.

Añadiendo artículos

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 debe llamarse cada vez que se golpea un punto final HTTP particular
Primero, definamos algunas constantes y escribamos la 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('/')
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  
El requestmódulo se utiliza para analizar la solicitud y obtener datos del cuerpo HTTP o los parámetros de consulta de la URL. responseSe utiliza para devolver una respuesta al cliente. La respuesta es de tipo JSON .
Si desea leer más sobre Lectura y escritura 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. La json.dumps()función convierte el objeto o diccionario 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 artículo como el 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 de ellos:
$ 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 ruta ( /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 el Frasco"}
A medida que despedimos la solicitud debemos ser recibidos con 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 elemento a la lista de tareas pendientes.

Recuperar todos los artículos

A menudo deseamos obtener todos los artículos 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 SELECTSi estamos interesados ​​en un solo artículo podemos llamar en su lugar c.fetchone().
Nuestro método, get_all_itemsdevuelve un objeto Python que contiene 2 elementos:
  • El número de artículos devueltos por esta consulta
  • Los artículos reales devueltos por la consulta
En main.py, definiremos una ruta /item/newque acepte una solicitud GET . Aquí no pasaremos el methodsargumento de la palabra clave a @app.route(), porque si omitimos este parámetro, entonces se establece de forma predeterminada 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
Debemos ser recibidos con la respuesta:
json {"count": 2, "items": [["Setting up Flask", "Not Started"], [Implement POST endpoint", "Not Started"]]}

Obtención del estado de los elementos individuales

Como hicimos con el ejemplo anterior, escribiremos una función de ayuda 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 main.pypara analizar la solicitud y atender la respuesta. Necesitamos la ruta para aceptar una solicitud GET y el nombre del elemento debe enviarse como un parámetro de consulta.
Se pasa un parámetro de consulta en el formato ?name=valuecon la URL. ej http://base-url/path/to/resource/?name=valueSi hay espacios en el valor , debe reemplazarlos con cualquiera +o con %20, que es la versión codificada en URL de un espacio. Puede tener varios pares nombre-valor separándolos con el &carácter.
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
De nuevo, usemos cURL para disparar la solicitud:
$ curl -X GET http://127.0.0.1:5000/item/status?name=Setting+up+Flask
Debemos ser recibidos con la respuesta:
{"status": "Not Started"}

Actualizando artículos

Ya que hemos completado la tarea "Configuración del matraz" hace un tiempo, es hora de que debamos actualizar su estado a "Completado".
Primero, escribamos una función helper.pyque ejecute 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 confiar en los comentarios del usuario y hacer nuestras validaciones, ya que nunca sabemos qué puede 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 los ataques de inyección de SQL .
A continuación, configuraremos una ruta main.pyque acepte 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'
Debemos ser recibidos con la respuesta:
{"Setting up Flask": "Completed"}

Eliminar elementos

Primero, escribiremos una función helper.pyque ejecute 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. Necesitamos pasar execute()una tupla incluso si solo hay un elemento en la tupla. Agregar la coma hace que esto se convierta en una tupla.
A continuación, configuraremos una ruta main.pyque acepte 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 borrado:
$ curl -X DELETE http://127.0.0.1:5000/item/remove -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json'
Debemos ser recibidos con la respuesta:
{"item": "Temporary item to be deleted"}
¡Y eso completa 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 crear una aplicación web sencilla basada en REST. Si tienes experiencia con otros frameworks de Python como Django, es posible que hayas observado que es mucho más fácil usar Flask.
Este tutorial se centró más en el aspecto de back-end de la aplicación, sin GUI, aunque también puede usar Flask para representar páginas HTML y plantillas, que guardaremos para otro artículo.
Si bien es perfectamente correcto usar Flask para administrar plantillas HTML, la mayoría de la gente usa Flask para crear servicios de back-end y construir la parte frontal de la aplicación mediante el uso de cualquiera de las bibliotecas de JavaScript populares. Puedes probar lo que mejor te funcione. ¡Buena suerte en tu viaje Frasco!
Si desea jugar con el código fuente o tiene dificultades para ejecutarlo desde el código anterior, ¡aquí está en GitHub !

Acerca de: Programator

Somos Instinto Programador

0 comentarios:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Con tecnología de Blogger.