Servicio REST simple en Flask
Holas, el día de hoy les voy a explicar cómo hacer un pequeño servicio REST en Flask usando una variable interna para guardar nuestros datos, dicho proyecto no es más que un CRUD simple de ToDo's
Estructura del Proyecto
Debido a que flask es muy flexible podemos organizar nuestro proyecto de diferentes maneras como la siguiente: http://exploreflask.com/en/latest/organizing.html
run.py # Archivo que inicia el servicio
requirements.txt # Archivo de requerimientos
app/
__init_.py # Permite sabe que es un paquete de Python
utils/
__init__.py
json_utils.py # Una pequeña utilidad para saber si un request recibido tiene un JSON
views/
__init__.py
todo.py # Las rutas del Servicio junto con sus funciones
Inicio
El archivo run.py
nos permite correr nuestro servicio, en él importamos el paquete app
y procedemos a poner el host y el puerto que queramos usar para su ejecución
import os
from app import app
app.debug = True
# Host es 0.0.0.0 para permitir accesos desde otras computadoras,
# de lo contrario sólo nuestra computadora local podrá acceder al servicio
host = os.environ.get('IP', '0.0.0.0')
port = int(os.environ.get('PORT', 8085))
app.run(host=host, port=port)
Al importar nuestro paquete app
veremos que el archivo init.py
tiene algo de código en su interior, este código se ejecutará la primera vez que importemos el paquete
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # Cors nos permite usar el servicio REST con un frontend como Angular, VueJS, etc
from app.views import todo # Importamos las vistas de nuestro Servicio
Vistas
En este servicio dentro del paquete views tenemos el archivo todo.py
que es donde se encuetran todas las rutas que posee el servicio así como las funciones asociadas a los mismos
import json
from flask import abort
from flask import request
from app import app
from ..utils import json_utils
todo_list = [] # Dentro de este array se van a guardar todos nuestros ToDo's
# La ruta para obtener todos los ToDo's es /
@app.route("/", methods=['GET'])
def get_all_todos():
# Retornamos una lista con los JSON guardados,
# si no hay nada retornará una lista vacía
return json.dumps([json.loads(each_todo) for each_todo in todo_list])
# Ruta para retornar un ToDo específico mediante su ID
@app.route("/" + '<string:todo_id>', methods=['GET'])
def get_todo(todo_id):
selected_todo = None
# Buscamos en la lista el ToDo que contenga el ID especificado
for element in todo_list:
if str(json.loads(element)['id']) == todo_id:
selected_todo = element
# Revisamos si existe el ToDo, caso contrario retornamos 404 -> NotFound
if selected_todo is None:
abort(404)
return selected_todo
# Ruta para guardar un nuevo ToDo
@app.route("/", methods=['POST'])
def post_todo():
# Revisamos si el request es un JSON válido
json_utils.is_not_json_request(request)
id_list = []
# Recorremos la lista de ToDo's y lo agregamos a
# una lista que contiene solo ID's
for element in todo_list:
id_list.append(json.loads(element)['id'])
# Ordenamos la lista de ID's
id_list.sort()
# Añadimos un ID nuevo a nuestro request
request.json['id'] = id_list[-1] + 1 if len(id_list) > 0 else 1
# Añadimos el JSON a nuestra lista
todo_list.append(str(json.dumps(request.json)))
return json.dumps(request.json)
# Ruta para actualizar un ToDo mediante su ID
@app.route("/" + '<string:todo_id>', methods=['PUT'])
def put_todo(todo_id):
# Revisamos si el request es un JSON válido
json_utils.is_not_json_request(request)
selected_todo = None
for element in todo_list:
if str(json.loads(element)['id']) == todo_id:
selected_todo = element
# Revisamos si existe el ToDo, caso contrario retornamos 404 -> NotFound
if selected_todo is None:
abort(404)
# Borramos el ToDo de la lista
todo_list.pop(todo_list.index(selected_todo))
request.json['id'] = int(todo_id)
# Añadimos el ToDo a nuestra lista con el ID al que pertenecía
todo_list.append(str(json.dumps(request.json)))
return json.dumps(request.json)
# Ruta para borrar un ToDo mediante su ID
@app.route("/" + '<string:todo_id>', methods=['DELETE'])
def delete_todo(todo_id):
selected_todo = None
# Buscamos en la lista el ToDo que contenga el ID especificado
for element in todo_list:
if str(json.loads(element)['id']) == todo_id:
selected_todo = element
# Revisamos si existe el ToDo, caso contrario retornamos 404 -> NotFound
if selected_todo is None:
abort(404)
# Borramos el ToDo de la lista
todo_list.pop(todo_list.index(selected_todo))
return ""
Utilitarios
Este servicio REST contiene un pequeño utilitario para controlar que un request sea un JSON válido
from flask import abort
# Revisamos que el request sea un JSON válido
# caso contrario retornamos un error 400 -> BadRequest
def is_not_json_request(request):
if not request.json:
abort(400)
Correr el servicio
Para correr este servicio necesitamos que nuestro Entorno Virtual de Python tenga las dependencias instaladas, para ello hacemos lo siguiente dentro de nuestro entorno virtual:
pip install -r requirements.txt
El archivo de requirements contiene las dependencias necesarias para poder correr este proyecto
Para correr el proyecto nos basta con:
python run.py
No olvidar que debemos correrlo con nuestro entorno virtual
Uso de la aplicación
Para probarlo podemos usar Insomnia REST Client, Postman, CURL, etc. Personalmente uso Insomnia, es opensource y muy bueno para trabajo con servicios REST
GET ToDo's -> Retornar una lista de ToDo's
Respuesta:
[
{
"title": "Nuevo ToDo",
"description": "Descripcion de ToDo",
"id": 1
}
]
GET ToDo especificado -> Retornar un ToDO por su ID
URL: http://127.0.0.1:8085/{id}
-> Reemplazar {id}
por el id que queremos, en este caso 1
Respuesta:
{
"title": "Nuevo ToDo",
"description": "Descripcion de ToDo",
"id": 1
}
POST ToDo -> Guardar
Respuesta:
{
"title": "Nuevo ToDo 1",
"description": "Descripcion de ToDo 1"
}
PUT ToDo -> Actualizar
URL: http://127.0.0.1:8085/{id}
-> Reemplazar {id}
por el id que queremos, en este caso 1
Respuesta:
{
"title": "Nuevo ToDo Actualizado",
"description": "Descripcion de ToDo Actualizada"
}
DELETE ToDo -> Borrar
URL: http://127.0.0.1:8085/{id}
-> Reemplazar {id}
por el id que queremos, en este caso 1
Respuesta: Vacía con código 200
La URL del proyecto se encuentra en: https://github.com/nano-bytes/flask/tree/master/simple-todo
Esto ha sido todo por hoy.
Happy Hacking!!