The previous article explained how to build the RESTful API using HTTP with the GET, POST, PUT and DELETE methods, without using a library for REST.

This article will use the Flask-RestFul library; you can check its documentation here (broken link).

Project file and directory structure

The file and directory structure remains the same as in the previous article:

tutorial-flask/
├── app
│   └── run.py
├── docker-compose.yml
├── Dockerfile
└── README.md

Dockerfile and docker-compose.yml files

The Dockerfile now also installs flask-restful; the file is shown below:

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


RUN pip3 install Flask-restful


EXPOSE 5000


ADD ./app/* /code/


COPY ./app/* /code/


CMD python run.py

The docker-compose.yml file is the same as in the previous article:

flask-rest1:

  build: .

  ports:

    - "5000:5000"

  volumes:

    - "./app/:/code"

  links:

    - mongo

mongo:

  image: mongo

  ports:

    - "27017:27017"

  volumes:

    - "/srv/data/db:/data/db:rw"

Application code

The same HTTP methods as in the previous article (GET, POST, PUT and DELETE) will be used; the url for the queries will be /empleados and the way to pass arguments through the url is by passing the employee's name /empleados/string:nombre .

Two classes are created:

  • EmpleadoList: This class receives no arguments through the URL; it has two methods, GET and POST:
    • GET: Lists the employees that exist in the database.
    • POST: Inserts an employee into the database by passing a json with the data.
  • Empleado: whose methods receive the name of an employee. The GET, PUT and DELETE methods are defined:
    • GET: Looks up an employee by passing their name; a json with their data is returned.
    • PUT: Updates an employee's information by passing their name and the json with the data to modify; it returns a json with all the employees.
    • DELETE: Deletes an employee from the database; the employee's name is passed in, and it returns a json with all the employees.

The code of app/run.py is shown below:



#!/usr/bin/env python


#Se importa Flask, reqest y jsonify


from flask import Flask, request,jsonify, Response



#Se importa MongoAlchemy


from flask_mongoalchemy import MongoAlchemy


#Se importa dumps


from bson.json_util import dumps


#rom flask_restful import Resource, Api


from flask_restful import reqparse, abort, Api, Resource




#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 asocia el API a la aplicacion


api = Api(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 de pagina no encontrada.


@app.errorhandler(404)


def not_found(error=None):


    mensaje = {


            'status': 404,


            'message': 'Not Found: ' + request.url,


    }


    resp = Response(jsonify(mensaje),status=404,mimetype='application/json')





    return resp





#Clase EmpleadosList que permite listar los empleados o insertar un empledo.


#Se definen los metdos get y post


class EmpleadosList(Resource):


    #Se define el metodo get el cual devuelve un json con todos los empleados.


    def get(self):


        #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())


            resp =  Response(dumps(resultado),status=200,mimetype='application/json')


            resp.headers['Link'] = 'http://blog.crespo.org.ve'


            return resp


        except (AttributeError):


            return not_found



    #Se define el metodo post para agregar un empleado por medio de un json a la


    #base de datos mongoDB.


    def post(self):


        #args = parser.parse_args()


        #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())


        resp =  Response(dumps(listado),status=201,mimetype='application/json')


        resp.headers['Link'] = 'http://blog.crespo.org.ve'


        return resp


#Se crea la Clase Empleado que hereda de Resource


#Tiene los metodos get, put y delete


class Empleado(Resource):


    #Se define el metodo get, permite buscar un empleado por su nombre


    def get(self,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({'nombre':resultado.nombre,'sexo':resultado.sexo,'edad':resultado.edad,'dni':resultado.dni}),200,{'Content-Type':'application/json'}


        except (AttributeError):


            return not_found

    #Se define el metodo put que permite actualizar la informacion de un empleado


    #pasando su nombre, los datos a modificar se pasan en un json.


    def put(self,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'])


            resultado.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.


            resp =  Response(dumps(listado),status=201,mimetype='application/json')


            resp.headers['Link'] = 'http://blog.crespo.org.ve'


            return resp


        except (AttributeError):


            return not_found


    #Se define el metodo delete que permite borrar un empleado de la base de datos


    #pasando el nombre del empleado.


    def delete(self,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())


            resp =  Response(dumps(listado),status=200,mimetype='application/json')


            resp.headers['Link'] = 'http://blog.crespo.org.ve'


            return resp


        except (AttributeError):


            return not_found


#Se define las rutas para los recursos con las clases asociadas:


#/empleado


#/empleado/<string:nombre>


api.add_resource(EmpleadosList,'/empleado')


api.add_resource(Empleado,'/empleado/<string:nombre>')


if __name__ == "__main__":


    #Se corre la aplicacion en modo debug


    app.run(host="0.0.0.0",debug=True)

Building the image and running the container

Building the Docker image:

docker-compose build

Running the Docker container:

docker-compose up

Testing the RESTful API

Postman will be used to query the API.

List all the employees

Open postman at the url http://localhost:5000/empleado with the GET method; a screenshot is shown below:

The JSON that is returned is the following:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]

Add an employee

Open postman at the URL http://localhost:5000/empleado with the POST method and pass the following JSON:

{
 "nombre": "Nadir",
 "sexo": "Masculino",
 "edad": 45,
 "dni": 11059
}

The screenshot of the data entry is shown below:

When the action is executed, the following result is obtained (as the next screenshot shows):

The JSON that is returned is the following:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}, {"edad": 45, "sexo": "Masculino", "nombre": "Nadir", "dni": 11059, "_id": {"$oid": "57fe2800d76747000b2ef40f"}}]

Find an employee

To find an employee, pass the following URL http://localhost:5000/empleado/Nadir with the GET method to postman; a screenshot of the result is shown below:

The JSON that is returned is the following:

"{\"edad\": 45, \"sexo\": \"Masculino\", \"nombre\": \"Nadir\", \"dni\": 11059}"

Update an employee

To update the employee Nadir, pass the following url to postman http://localhost:5000/empleado/Nadir with the PUT method, and pass the following json:

{
 "sexo": "Masculino",
 "edad": 35,
 "dni": 11059
}

The screenshot shows the data that was loaded into postman:

When the action is executed, the following screenshot is obtained:

The JSON that is returned is the following:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}, {"edad": 35, "sexo": "Masculino", "nombre": "Nadir", "dni": 11059, "_id": {"$oid": "57fe2800d76747000b2ef40f"}}]

As you can see, the data of the employee Nadir, as far as their age is concerned, has changed.

Delete an employee

Finally, the employee Nadir will be deleted from the database; pass the url http://localhost/empleado/Nadir with the DELETE method; the result in postman is shown below:

The JSON that is returned is the following:

[{"edad": 29, "sexo": "Femenino", "nombre": "Jane Doe", "dni": 8, "_id": {"$oid": "57ebbce45fd2bbeffc51330b"}}, {"edad": 39, "sexo": "Masculino", "nombre": "John Doe", "dni": 7, "_id": {"$oid": "57ebbd195fd2bbeffc51330c"}}, {"edad": 55, "sexo": "Masculino", "nombre": "Pedro Perez", "dni": 6, "_id": {"$oid": "57ebbd505fd2bbeffc51330d"}}, {"edad": 65, "sexo": "Femenino", "nombre": "Petra", "dni": 5, "_id": {"$oid": "57ebbd6b5fd2bbeffc51330e"}}, {"edad": 18, "sexo": "Masculino", "nombre": "Luis Gonzalez", "dni": 4, "_id": {"$oid": "57ebc34d5fd2bbeffc51330f"}}, {"edad": 34, "sexo": "Femenino", "nombre": "Luissana", "dni": 2, "_id": {"$oid": "57ebc3935fd2bbeffc513311"}}, {"edad": 42, "sexo": "Masculino", "nombre": "Neg", "dni": 1, "_id": {"$oid": "57ebc4b85fd2bbeffc513312"}}, {"edad": 29, "sexo": "Femenino", "nombre": "Dayana", "dni": 1050, "_id": {"$oid": "57f64d7d557e3f00086651e8"}}]

As you can see, the employee Nadir no longer exists in the database.

Finally, the screenshot of the running container is shown:

The Flask execution output is kept highlighted (the text is shown below):

flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:02:56] "GET /empleado HTTP/1.1" 200 -
mongo_1       | 2016-10-12T12:09:37.175+0000 I COMMAND  [conn2] update empleados.empleados query: { _id: ObjectId('57fe2800d76747000b2ef40f') } update: { _id: ObjectId('57fe2800d76747000b2ef40f'), edad: 45, sexo: "Masculino", nombre: "Nadir", dni: 11059 } keysExamined:0 docsExamined:0 nMatched:1 nModified:1 upsert:1 keyUpdates:0 writeConflicts:0 numYields:1 locks:{ Global: { acquireCount: { r: 2, w: 2 } }, Database: { acquireCount: { w: 2 } }, Collection: { acquireCount: { w: 2 } } } 326ms
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:09:37] "POST /empleado HTTP/1.1" 201 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:12:40] "GET /empleado/Nadir HTTP/1.1" 200 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:18:23] "POST /empleado/Nadir HTTP/1.1" 405 -
mongo_1       | 2016-10-12T12:20:00.777+0000 I COMMAND  [conn2] update empleados.empleados query: { _id: ObjectId('57fe2800d76747000b2ef40f') } update: { _id: ObjectId('57fe2800d76747000b2ef40f'), edad: 35, sexo: "Masculino", nombre: "Nadir", dni: 11059 } keysExamined:1 docsExamined:1 nMatched:1 nModified:1 keyUpdates:0 writeConflicts:0 numYields:2 locks:{ Global: { acquireCount: { r: 3, w: 3 } }, Database: { acquireCount: { w: 3 } }, Collection: { acquireCount: { w: 3 } } } 124ms
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:20:00] "PUT /empleado/Nadir HTTP/1.1" 201 -
flask-rest1_1 | 172.17.0.1 - - [12/Oct/2016 12:23:19] "DELETE /empleado/Nadir HTTP/1.1" 200 -

This improves how to create a REST API, in this case using flask-restful.

The project repository is in the gitlab repository tutorial-flask on the branch mongo-flask-restful.

Make a donation! If you liked the article you can make a donation with Bitcoin (BTC) using the digital wallet of your choice to the following address: 17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV

Or by scanning the QR code from your wallet:

17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV