En artículo anterior (enlace roto) se hizo un CRUD para Flask con MongoDB usando Mongo-Alchemy, en dicho artículo se manejaron varios URL para hacer el CRUD.
En este artículo se manejará el REST por medio de los URL y con Flask solamente, en próximos artículos se usará Flask-RESTful y Flask-Restless.
Métodos de HTTP
En este caso se va a estandarizar el CRUD por medio de API Rest Ful, donde se usará el mismo URL con métodos GET, POST, PUT y DELETE.
Los métodos se usarán de la siguiente forma:
- Agregar Empleado: Método POST, URL /empleado, sin parámetros.
- Buscar Empleado: Método GET, URL /empleado, parámetro string:nombre.
- Listar Empleados: Método GET, URL /empleado.
- Editar/Actualizar Empleado: Método PUT, URL /empleado, parámetro string:nombre.
- Borrar Empleado: Método DELETE, URL /empleado, parámetro string:nombre.
Como puede notarse, con un sólo URL se tiene las acciones del CRUD usando los métodos de HTTP, esto permite estándarizar y simplificar el uso de los URL, y principalmente hacer un correcto uso de HTTP:
Para más información sobre el estilo arquitectónico API REST pueden revisar en wikipedia.
Estructura de directorios y archivos Se mantiene la misma estructura de archivos y directorios del artículo sobre CRUD:
tutorial-flask ├── app │ └── run.py ├── docker-compose.yml ├── Dockerfile └── README.md
Archivo Dockerfile y docker-compose.yml
El archivo Docker contiene lo siguiente:
FROM python WORKDIR /code/ RUN pip3 install --upgrade pip RUN pip3 install pymongo RUN pip3 install Flask RUN pip3 install Flask-PyMongo RUN pip3 install Flask-MongoAlchemy EXPOSE 5000 ADD ./app/* /code/ COPY ./app/* /code/ CMD python run.py
El archivo docker-compose.yml contiene lo siguiente:
flask-rest1: build: . ports: - "5000:5000" volumes: - "./app/:/code" links: - mongo mongo: image: mongo ports: - "27017:27017" volumes: - "/srv/data/db:/data/db:rw"
Codigo de la aplicación
El código de run.py se muestra a continuación:
#!/usr/bin/env python #Se importa Flask, reqest y jsonify from flask import Flask, request,jsonify #Se importa MongoAlchemy from flask_mongoalchemy import MongoAlchemy #Se importa dumps from bson.json_util import dumps #Se instancia la clase de Flask, se configura el acceso #a la base de datos mongodb a empleados app = Flask(__name__) app.config['MONGOALCHEMY_DATABASE'] = 'empleados' app.config['MONGOALCHEMY_CONNECTION_STRING'] = 'mongodb://mongo:27017/empleados' #Se instancia mongoalchemy pasando la app. db = MongoAlchemy(app) #Se crea la clase empleados la cual manejara los documentos. class empleados(db.Document): nombre = db.StringField() sexo = db.StringField() edad = db.IntField() dni = db.IntField() #Se define la funcion agregar con metodo get @app.route('/empleado',methods=['POST']) def agregar(): #Se crea la instancia empleado de la clase empleados donde se #logra hacer la inserción de un empleado con el metodo save. nombre = str(request.json['nombre']) sexo = str(request.json['sexo']) edad = int(request.json['edad']) dni = int(request.json['dni']) empleado = empleados(nombre=nombre,sexo=sexo,edad=edad,dni=dni) empleado.save() #Se retorna que el usuario fue agregado. ### consulta = empleados.query.all() listado = [] for i in consulta: listado.append(i.wrap()) return dumps(listado) #Se crea la funcion buscar con metodo get. @app.route('/empleado/<string:nombre>',methods=['GET']) def buscar(nombre): #Se realiza la busqueda y se devuelve el resultado, si existe un error de atributo (que el empleado no existe) #Se devuelve empleado no encontrado. try: resultado = empleados.query.filter(empleados.nombre == nombre).first() return dumps({'resultado':{'nombre':resultado.nombre,'sexo':resultado.sexo,'edad':resultado.edad,'dni':resultado.dni}}) except (AttributeError): return dumps({'resultado': 'Empleado no encontrado'}) @app.route('/empleado',methods=['GET']) def listar(): #Se realiza la busqueda y se devuelve el resultado, si existe un error de atributo (que el empleado no existe) #Se devuelve empleado no encontrado. try: consulta = empleados.query.all() resultado = [] for i in consulta: resultado.append(i.wrap()) return dumps(resultado) except (AttributeError): return dumps({'resultado': 'Empleado no encontrado'}) #Se crea la funcion actualizar que tiene metodo put, con #url /empleado y se le pasa el nombre a buscar @app.route('/empleado/<string:nombre>',methods=['PUT']) def actualizar(nombre): #Se intenta buscar al empleado en la base de datos, si no esta devuelve error try: #Se consulta en la base de datos, donde devuelve el primer elemento encontrado resultado = empleados.query.filter(empleados.nombre == nombre).first() #Se toma los datos de un json y se guardan en sus variables, salvando luego #en la base de datos. resultado.sexo = str(request.json['sexo']) resulado.edad = int(request.json['edad']) resultado.dni = int(request.json['dni']) resultado.save() #Se realiza la consulta desplegando los empleados consulta = empleados.query.all() listado = [] for i in consulta: listado.append(i.wrap()) #Se devuelve la nueva lista de empleados en un json. return dumps(listado) except (AttributeError): return dumps({'resultado': 'Empleado no encontrado'}) #Borrar un empleado de la base de datos, se pasa el url /empleado con el #string nombre usando el metodo delete. @app.route('/empleado/<string:nombre>',methods=['DELETE']) def borrar(nombre): #Se busca el empleado, si existe se borra de la base de datos y se devuelve #mensaje de empleado borrado, si no, se devuelve el mensaje de empleado no #encontrado. try: resultado = empleados.query.filter(empleados.nombre == nombre).first() resultado.remove() ### consulta = empleados.query.all() listado = [] for i in consulta: listado.append(i.wrap()) return dumps(listado) except (AttributeError): return dumps({'resultado':'Empleado no encontrado'}) if __name__ == "__main__": #Se corre la aplicacion en modo debug app.run(host="0.0.0.0",debug=True)
Construcción de la imagen y ejecución del contenedor
Construcción de la imagen Docker:
docker-compose build
Ejecución del contenedor:
docker-compose up
Pruebas del API REST FUL
Para este caso se usará POSTMAN, una aplicación para google chrome.
Listar Empleados
Se usa postman colocando 127.0.0.1:5000/empleados con método GET, en la siguiente figura se muestra el listado:
El JSON que devuelve es el siguiente:
[{"edad": 29, "dni": 8, "sexo": "Femenino", "nombre": "Jane Doe", "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "dni": 7, "sexo": "Masculino", "nombre": "John Doe", "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "dni": 6, "sexo": "Masculino", "nombre": "Pedro Perez", "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "dni": 5, "sexo": "Femenino", "nombre": "Petra", "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "dni": 4, "sexo": "Masculino", "nombre": "Luis Gonzalez", "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "dni": 2, "sexo": "Femenino", "nombre": "Luissana", "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 43, "dni": 1, "sexo": "Masculino", "nombre": "Neg", "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "dni": 1050, "sexo": "Femenino", "nombre": "Dayana", "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]
Buscar un Empleado
En el postman se coloca el siguiente url con método GET 127.0.0.1/empleado/Neg , la siguiente figura muestra el resultado:
EL JSON que devuelve es el siguiente:
{"resultado": {"edad": 43, "dni": 1, "sexo": "Masculino", "nombre": "Neg"}}
Agregar Empleado
En postman se agrega el url con método POST 127.0.0.1:5000/empleado y se pasa en formato JSON lo siguiente:
{ "nombre": "Fatima", "sexo": "Femenino", "edad": 24, "dni": 1052 }
La siguiente figura muestra la captura de la información que se coloca en postman:
Esto devuelve el siguiente JSON:
[{"edad": 29, "dni": 8, "sexo": "Femenino", "nombre": "Jane Doe", "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "dni": 7, "sexo": "Masculino", "nombre": "John Doe", "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "dni": 6, "sexo": "Masculino", "nombre": "Pedro Perez", "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "dni": 5, "sexo": "Femenino", "nombre": "Petra", "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "dni": 4, "sexo": "Masculino", "nombre": "Luis Gonzalez", "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "dni": 2, "sexo": "Femenino", "nombre": "Luissana", "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 43, "dni": 1, "sexo": "Masculino", "nombre": "Neg", "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "dni": 1050, "sexo": "Femenino", "nombre": "Dayana", "_id": {"$oid": "57f64d7d557e3f00086651e8"}}, {"edad": 24, "dni": 1052, "sexo": "Femenino", "nombre": "Fatima", "_id": {"$oid": "57f68505557e3f000e1aa766"}}]
Como se nota el empleado Fátima aparece en la base de datos al final.
####Borrar Empleado Para este caso se buscará borrar al empleado Fátima, se coloca el siguiente url en postman con método DELETE: 127.0.0.1:5000/empleado/Fatima .
La siguiente imagen muestra la ejecución en postman:
Esto devuelve el siguiente JSON:
[{"edad": 29, "dni": 8, "sexo": "Femenino", "nombre": "Jane Doe", "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "dni": 7, "sexo": "Masculino", "nombre": "John Doe", "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "dni": 6, "sexo": "Masculino", "nombre": "Pedro Perez", "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "dni": 5, "sexo": "Femenino", "nombre": "Petra", "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "dni": 4, "sexo": "Masculino", "nombre": "Luis Gonzalez", "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "dni": 2, "sexo": "Femenino", "nombre": "Luissana", "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 43, "dni": 1, "sexo": "Masculino", "nombre": "Neg", "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "dni": 1050, "sexo": "Femenino", "nombre": "Dayana", "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]
Modificar empleado
Para este caso se modificará el empleado Neg, cambiando su edad.
Se abre el postman colocando el url 127.0.0.1:5000/empleado/Neg con método PUT. Adicional se agrega el JSON:
{ "sexo": "Masculino", "edad": 41, "dni": 1 }
La siguiente imagen muestra el postman:
Esto devuelve el siguiente JSON:
[{"sexo": "Femenino", "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}, "nombre": "Jane Doe", "dni": 8, "edad": 29}, {"sexo": "Masculino", "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}, "nombre": "John Doe", "dni": 7, "edad": 39}, {"sexo": "Masculino", "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}, "nombre": "Pedro Perez", "dni": 6, "edad": 55}, {"sexo": "Femenino", "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}, "nombre": "Petra", "dni": 5, "edad": 65}, {"sexo": "Masculino", "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}, "nombre": "Luis Gonzalez", "dni": 4, "edad": 18}, {"sexo": "Femenino", "_id": {"$oid": "57ebc3935fd2bbeffc513311"}, "nombre": "Luissana", "dni": 2, "edad": 34}, {"sexo": "Masculino", "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}, "nombre": "Neg", "dni": 1, "edad": 41}, {"sexo": "Femenino", "_id": {"$oid": "57f64d7d557e3f00086651e8"}, "nombre": "Dayana", "dni": 1050, "edad": 29}]
Se muestra en subrayado el empleado Neg que cambió su edad en la base de datos.
Para terminar, ahora se muestra la salida del servidor:
flask-rest1_1 | 172.17.0.1 - - [06/Oct/2016 15:59:33] "GET /empleado HTTP/1.1" 200 - flask-rest1_1 | 172.17.0.1 - - [06/Oct/2016 16:03:48] "GET /empleado/Neg HTTP/1.1" 200 - flask-rest1_1 | 172.17.0.1 - - [06/Oct/2016 17:08:22] "POST /empleado HTTP/1.1" 200 - flask-rest1_1 | 172.17.0.1 - - [06/Oct/2016 17:39:24] "DELETE /empleado/Fatima HTTP/1.1" 200 - flask-rest1_1 | 172.17.0.1 - - [06/Oct/2016 17:47:59] "PUT /empleado/Neg HTTP/1.1" 200 -
Como se ve, la salida devuelve el URL que se usa y los métodos utilizados.
De esta manera se muestra el uso del estilo arquitectónico API REST y el uso de HTTP con sus diferentes métodos.
El código fuente de este artículo se encuentra en el repositorio tutorial-flask de gitlab en la rama mongo-restful.
¡Haz tu donativo! Si te gustó el artículo puedes realizar un donativo con Bitcoin (BTC) usando la billetera digital de tu preferencia a la siguiente dirección: 17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV
O Escaneando el código QR desde la billetera:
