martes, 5 de febrero de 2019

Crear una API REST con PYTHON y FLASK (Parte 1)


Vamos a crear un API REST muy sencillo con Python. Para ello utilizaremos una librería llamada Flask.

Flask es un microframework web para Python basado en Werkzeug y Jinja 2 que  nos provee de funcionalidades para construir aplicaciones web, manejo de peticiones HTTP con un enrutado sencillo para generar una API muy ligera y renderizado de templates.

Se pueden utilizar multitud de frameworks para crear aplicaciones web y APIs con Python. La más conocida es Django, un framework muy completo que te genera la estructura del proyecto y que incluye muchos plugins y herramientas. Sin embargo para aplicaciones sencillas puede llegar a ser abrumador y Flask es capaz de armar una API sencilla en un único fichero de texto.


1. Primeros pasos


Lo primero que debemos hacer es comprobar que tenemos Python instalado. Abrimos un terminal y ejecutamos:

python --version    

En caso de estar instalado, la consola nos devolverá la versión de Python. En caso contrario, debemos instalarlo.

Python 3.6.5

Ahora instalaremos Flask utilizando pip, el gestor de paquetes de Python.

pip install flask

A continuación será necesario crearnos un directorio, una carpeta, para el proyecto. 

Como soy poco imaginativo, llamaré al proyecto “peliculas”, porque la API será de películas.
Dentro crearemos un fichero vacío y lo llamaremos “api.py”.

De tal manera que quedará:

peliculas/api.py

Ya tenemos todo listo para empezar a darle caña a Python.



2. Creando la app y el primer servicio tontorrón


Abrimos nuestro fichero en blanco “api.py”  y le copiamos el siguiente código. No os preocupéis, lo explicaré a fondo más adelante.


import flask

app = flask.Flask(__name__)
app.config["DEBUG"] = True

@app.route('/', methods=['GET'])
def home():
    return "<h1>Mi app de Pelis</h1><p>Este es mi sitio web super molón de películas de los 90.</p>"

app.run()


Y voila, ya has creado una aplicación en python. De momento sólo tiene un servicio, habrá que seguir trabajando en ella.

·         Probando los avances:


Para que todo esto funcione debemos ejecutar nuestro archivo de Python. Para ello, volvemos a nuestro terminal abierto, nos situamos en el archivo del proyecto y lanzamos el proceso, como cualquier script de Python, escribiendo:

python api.py

El terminal nos debe devolver algo como:

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

¿Qué sucede ahora? Que podemos invocar esa url con un método get desde Postman, o simplemente entrar a un navegador, el Chrome por ejemplo, el que utilices por defecto, y acceder a esta url.

o   http://127.0.0.1 hace referencia a nuestro entorno local. De hecho, puede sustituirse por localhost: localhost:5000

o   :500 hace referencia al puerto donde estamos escuchando las peticiones HTTP a nuestra API




Enhorabuena, parece que todo ha ido bien.

·         ¿Qué hace mi código?


Vamos a explicar que está ocurriendo aquí:

Primero se importa la librería de flask, esto quiere decir, que tendremos acceso desde nuestro proyecto a todo el código de esta librería, que hará el trabajo sucio por nosotros.


import flask


Flask está mapeando las peticiones HTTP a funciones de Python. En este caso, hemos mapeado el path  “/” a la función home.


@app.route('/', methods=['GET'])
def home():
    return "<h1>Mi app de Pelis</h1><p>Este es mi sitio web super molón de películas de los 90.</p>"


Cuando nos conectamos a http://127.0.0.1:5000/, lo cual es  la  url donde lanzamos el proyecto (http://127.0.0.1:5000) más el path (/) al que lanzamos la petición, Flask mira a ver si el path solicitado coincide con alguno de los declarados. Como hemos declarado el path “/” Flask lo localiza y ejecuta la función home, la cual devuelve un par de líneas html renderizadas.
A todo este proceso se le llama enrutamiento, o routing.


app = flask.Flask(__name__)
app.config["DEBUG"] = True


Lo que ocurre en estas líneas es sencillo. Flask crea el objeto aplicación como la variable global app. Este objeto contiene toda la información y los métodos que hacen que la aplicación realice diferentes acciones. Por ejemplo, la última línea de código app.run(), que le indica a la aplicación que debe iniciarse, es uno de estos métodos.

En la segunda de estas líneas se especifica que la aplicación debe iniciarse en modo debug. De esta manera se nos informará desde el terminal de cualquier error o información extra. Muy útil en desarrollo, pero que debemos deshabilitar a la hora de subir nuestra aplicación a producción.

Ahora que sabemos cómo arrancar la aplicación y crear servicios, vamos a intentar que nuestra API sirva para algo.



3. Devolviendo datos desde la API


La principal función de una API es la de proveer información a los clientes que la invocan. Esta información no es más que datos persistentes almacenados en una base de datos o consultados en otra API, pudiendo sufrir transformaciones, dependiendo de las exigencias del servicio. También en algunos casos, se crean servicios para que los clientes puedan modificar dicha información.

Vamos a hacer un pequeño ejemplo de cómo nuestra API puede suministra datos mediante una llamada GET.

De momento ni siquiera vamos a consultar base de datos. Vamos a añadir nuestros datos a una lista de diccionarios de Python, que no son más que objetos con un par de clave (key) – valor (value). Algo así:


{
  'key1': 'value1',
  'key2': 'value2'
{


Siguiendo la ingeniosa idea de las pelis, nuestro array de diccionarios podría ser el siguiente:

[
    {
     'title': 'Pulp Fiction',
     'author': 'Quentin Tarantino'
    {
     'title': 'Die Hard 3',
     'author': 'John McTiernan'
    }
]

Vamos a añadir esta información dentro de nuestro fichero api.py, la enriqueceremos un poco y la asignaremos a una variable. También crearemos un par de métodos para que pueda ser consultada.

Nuestro código ahora, sería el siguiente:


import flask
from flask import request, jsonify

app = flask.Flask(__name__)
app.config["DEBUG"] = True

films = [
    {'id': 0,
     'title': 'Pulp Fiction',
     'author': 'Quentin Tarantino',
     'year_published': '1994'},
    {'id': 1,
     'title': 'Die Hard 3',
     'author': 'John McTiernan',
     'published': '1995'},
    {'id': 2,
     'title': 'Jurassic Park',
     'author': 'Steven Spielberg',
     'published': '1993'}
]


@app.route('/', methods=['GET'])
def home():
    return "<h1>Mi app de Pelis</h1><p>Este es mi sitio web super molón de películas de los 90.</p>"

# A route to return all of the available entries in our catalog.
@app.route('/api/films/all', methods=['GET'])
def api_all():
    return jsonify(films)

@app.route('/api/films', methods=['GET'])
def api_id():
    # Check if an ID was provided as part of the URL.
    # If ID is provided, assign it to a variable.
    # If no ID is provided, display an error in the browser.
    if 'id' in request.args:
        id = int(request.args['id'])
    else:
        return "Error: No id field provided. Please specify an id."

    # Create an empty list for our results
    results = []

    # Loop through the data and match results that fit the requested ID.
    # IDs are unique, but other fields might return many results
    for film in films:
        if film['id'] == id:
            results.append(film)

    # Use the jsonify function from Flask to convert our list of
    # Python dictionaries to the JSON format.
    return jsonify(results)

app.run()

Hemos implementado dos nuevos servicios, uno para consultar todas las películas de nuestra aplicación, y otro para consultar película por película indicando el id.

·         Probando los avances:


Si ya tenemos el servidor lanzado desde el terminal, Flask actualizará automáticamente los cambios al guardarlos desde el editor, sin necesidad de reiniciar. En caso contrario, tenemos que volver a lanzarlo como ya vimos en la sección anterior.

De igual manera que antes hemos llamado a  http://127.0.0.1:5000/ y nos devolvía el HTML de la home de nuestra aplicación, ahora probaremos las siguientes llamadas:



o   localhost:5000/api/films/all

Recordemos que localhost hace referencia a nuestro local, igual que 127.0.0.1
Al realizar un GET a esta dirección obtendremos el array de diccionarios con toda la información de películas que tenemos almacenadas en nuestra app. Te aconsejo que lo pruebes.

o   localhost:5000/api/films?id=0

Si hacemos GET sobre esta url, nos devolverá únicamente la información correspondiente a la película almacenada con id igual a 0, en este caso, Pulp Fiction.
Ahora prueba a cambiar el valor del id en la url y verás como la información que nos llega va cambiando. ¿Qué pasaría si pedimos el id = 3? ¿Por qué?

·         ¿Qué hace mi código?


El primero de los servicios es muy sencillo. Nueva ruta, nueva función que devuelve información almacenada en la variable films, el diccionario con todas las películas, declarada mas arriba.

@app.route('/api/films/all', methods=['GET'])
def api_all():
    return jsonify(films)

Lo único novedoso es jsonify, que es una función que transforma nuestros objetos de Python a formato JSON, que es un formato de transferencia de datos.

El segundo servicio tiene un poquito más miga. Entra en juego el objeto request que nos permite acceder a la información de la llamada entrante, entre otras cosas los parámetros que le pasamos a la llamada, cabeceras, cuerpo de la llamada si lo hubiera, etc.

Tanto jsonify como request han sido importados de la librería flask al principio del código.

Así pues, esto nos permite acceder a la información almacenada en request.args que no es otra que la que hemos introducido en la url detrás del símbolo “?”, los argumentos de la llamada. Vamos a verlo en el código.


if 'id' in request.args:
        id = int(request.args['id'])
    else:
        return "Error: No id field provided. Please specify an id."


En request.args esperamos encontrar una varliable llamda id para asignarle el valor a nuestra variable id. En caso contrario, devolveríamos un mensaje de error.

Posteriormente recorreremos nuestros datos con un bucle for, nos quedaremos con el elemento cuyo id coincida, lo añadiremos a results y lo devolveremos con un return como un objeto JSON.

Hemos completado la parte primera del curso. Puedes descargar aquí todo el código que hemos escrito y probarlo.

Siguiente misión: Añadir conexión a Base de Datos.


Crear una API REST con PYTHON y FLASK (Parte 1)

Vamos a crear un API REST muy sencillo con Python. Para ello utilizaremos una librería llamada Flask . Flask es un microframework web p...