$30 off During Our Annual Pro Sale. View Details »

JUC2016 - Hands-on lab: Construyendo una API REST con Python y MongoDB

CETA-Ciemat
February 11, 2016

JUC2016 - Hands-on lab: Construyendo una API REST con Python y MongoDB

II Jornadas Técnicas UEx – CIEMAT. Hands-on lab: Construyendo una API Rest con MongoDB y Python

CETA-Ciemat

February 11, 2016
Tweet

More Decks by CETA-Ciemat

Other Decks in Technology

Transcript

  1. CONSTRUYENDO UNA API REST
    CON PYTHON Y MONGODB
    César Suárez Ortega
    II Jornadas Técnicas UEx – CIEMAT: Introducción a NoSQL con MongoDB
    9 – 11 de Febrero, 2016

    View Slide

  2. View Slide

  3. View Slide

  4. Software Developer / Researcher
    César Suárez Ortega
    tharandur
    csuarez

    View Slide

  5. Software Developer / Researcher
    César Suárez Ortega

    View Slide

  6. POWERED BY

    View Slide

  7. Software Developer / Researcher
    César Suárez Ortega

    View Slide

  8. Índice
    Qué es una API REST
     Python + MongoDB = PyMongo
    Práctica:
    API para una tienda online

    View Slide

  9. APIs REST

    View Slide

  10. View Slide

  11. APIs REST
     API: Application Programming Interface.
     REST: Representational State Transfer.
    Arquitectura cliente-servidor.
    Comunicación mediante protocolo HTTP.
    CLIENTE SERVIDOR
    Petición HTTP
    Respuesta HTTP

    View Slide

  12. Petición HTTP
     Se hace a una URL (http://api.com/foo).
    Métodos de petición:
     GET: Obtiene datos.
     POST: Edita datos
     PUT: Añade datos
     DELETE: Borra datos.
    Cabeceras, cuerpo (JSON), versión, etc.

    View Slide

  13. Respuesta HTTP
    Cuerpo (JSON)
    Código de respuesta
     200: OK
     4XX: Error en la petición.
     400: Petición mal formada.
     401: Problema en autenticacións.
     403: Petición rechazada.
     404: Recurso no encontrado.
     5XX: Error en servidor (500).
    Cabeceras, versión, etc.

    View Slide

  14. ¿Por qué REST?

    View Slide

  15. View Slide

  16. http://getpostman.com

    View Slide

  17. PyMongo

    View Slide

  18. Python

    View Slide

  19. Python 101
    def print_animals(print_it):
    if print_it == True:
    for animal in ["dog", "cat", "mouse"]:
    print "{0} is a mammal".format(animal)
    Lenguaje interpretado.
     No usa llaves: Identación.
    Muy muy legible.
     No hace falta saberlo para la práctica.
    https://learnxinyminutes.com/docs/python/

    View Slide

  20. http://bit.ly/juc-pymongo-code

    View Slide

  21. JSON = Python Dictionaries
    product = {
    "type": "videogame",
    "title": "The Witness",
    "price": 36.99,
    "description": "Best game ever",
    "platforms": ["PC", "PS4"],
    "reviews": [
    {
    "user": "csuarez",
    "rating": 5,
    "comment": "This game changes my life *_*"
    },
    {
    "user": "jtriguero",
    "rating": 5,
    "comment": "I see puzzles everywhere 8)"
    }
    ]
    }

    View Slide

  22. PyMongo 101
    # Importamos PyMongo
    from pymongo import MongoClient
    # Creamos el cliente
    client = MongoClient("0.0.0.0", 27017)
    # Obtenemos una base de datos
    db = client.shop
    # Obtenemos una colección
    collection = db.catalog

    View Slide

  23. Insertando datos
    from bson.objectid import ObjectId
    # Definiendo un documento
    item = {"description": "our first item"}
    # Insertando un documento
    objectid = collection.insert_one(item)
    # De ObjectId a string
    objectid_as_string = str(objectid)
    # De string a ObjectId
    objectid_is_back = ObjectId(objectid_as_string)
    # Array de documentos
    lots_of_items = [
    {"description": "another item"},
    {"description": "moar items"}
    ]
    # Insertando un array de documentos
    array_of_object_ids = collection.insert_many(lots_of_items)

    View Slide

  24. Consultando datos I
    # Obteniendo el primer documento
    document = collection.find_one()
    print document["description"]
    # Usando queries y proyecciones
    document = collection.find_one({"description": "another item"},
    "fields"={"_id": False})
    # ¡Organiza tu código!
    document = collection.find_one(
    {"description": "another item"}, fields={"_id": False}
    )
    # ¡En serio! >:
    document = collection.find_one(
    {"description": "another item"},
    fields={"_id": False}
    )

    View Slide

  25. Consultando datos II
    # Obteniendo varios documentos. ¡Devuelve un iterator!
    cursor = collection.find({})
    for doc in cursor:
    print doc['description']
    # Esto es lo mismo
    for doc in collection.find({}):
    print doc['description']
    # Dot Notation
    for doc in collection.find({'field1.field2': 'whatever'}):
    print doc['field1']['field2']
    # Filtrando por datos de un array
    for doc in collection.find({'some_array': 'value'}):
    print doc['some_array']

    View Slide

  26. Consultando datos III
    # Ordenando resultados
    for doc in col.find({}).sort('field'):
    ...
    # Ignorando los dos primeros elementos
    for doc in col.find({}).skip(2):
    ...
    # Devolviendo sólo 5 resultados
    for doc in col.find({}).limit(5):
    ...
    # ¡Todo junto!
    for doc in col.find({}).skip(2).limit(5).sort('field'):
    ...

    View Slide

  27. Consultando datos IV
    # Operadores ($lt, $lte, $gt, $gte)
    col.find_one({'stock': {'$lt': 100}})
    #Operadores sobre arrays
    col.find_one({'tags': '$in': ['mongodb','nosql']})
    #Operadores lógicos
    col.find_one({'$or': [
    {'stock': {'$lt': 100}},
    {'tags': '$in': ['mongodb','nosql']}
    ])

    View Slide

  28. Agrupando datos: Aggregation
    # Obtenemos un sumatorio para cada valor del campo 'tags'
    collection.aggregate([
    {'$unwind': '$tags'},
    {'$group': {
    '_id': '$tags',
    'total': {'$sum': 1}
    }}
    ])
    # Resultado esperado
    [
    {
    '_id': 'book', 'total': 2
    },
    {
    '_id': 'videogame', 'total': 4
    }
    ]

    View Slide

  29. Indexes
    from pymongo import ASCENDING
    #Creando un index
    col.create_index([("item_number", ASCENDING)])
    #Usando el index
    for doc in col.find({}).hint(["item_number", ASCENDING]):
    print doc['description']

    View Slide

  30. Actualizando datos I
    # Incrementando el valor de un documento
    col.update_one({"item_number": 5}, {"$inc": {"stock": 20}})
    # Incrementando el valor de varios documentos
    col.update_many({"item_number": 5}, {"$inc": {"stock": 20}})
    # Cambiando el valor de un campo
    col.update_one({"item_number": 5}, {"$set": {"stock": 0}})
    # Añadiendo un valor a un array
    col.update_one({"item_number": 5}, {"$push": {"tag": "sweet"}})
    # Borrando un valor de un array
    col.update_many({}, {"$pull": {"tag": "sweet"}})
    # Reemplazando un documento (también hay replace_many)
    col.replace_one({"stock": 13}, {"stock": 0, "tags": ["cool"]})

    View Slide

  31. Actualizando datos II
    # Haciendo un upsert
    res = col.replace_one({"stock": 13}, {"stock": 0, "tags":
    ["cool"]}, True)
    print res.upserted_id
    # Consulta y actualización atómicas.
    doc = col.find_one_and_update({}, {"$inc": {"stock": 20})
    if doc == None:
    print "No doc to update!"
    # Devolviendo el documento nuevo
    from pymongo import ReturnDocument
    doc = col.find_one_and_update(
    {'_id': 'userid'},
    {'$inc': {'stock': 1}},
    return_document=ReturnDocument.AFTER
    )

    View Slide

  32. Borrando datos
    # Borrando un elemento
    col.delete_one({"item_number": 5})
    # Borrando muchos elementos
    col.delete_many({"stock": 0})
    # Consulta y borrado atómico
    doc = col.find_one_and_delete({"item_number": 5})

    View Slide

  33. PRÁCTICA
    #1

    View Slide

  34. View Slide

  35. View Slide

  36. API REST para una tienda online
    Gestión del catalógo y su stock
    Gestión de los carritos

    View Slide

  37. View Slide

  38. Esquema para los productos I
    {
    "type": "videogame", #obligatorio
    "title": "The Witness", #obligatorio
    "stock": 1200, #obligatorio
    "price": 36.90, #obligatorio
    "platforms": ["PS4", "PC"],
    "minimum_requirements": {
    "ram": "8GB",
    "cpi": "i7 3.5Ghz",
    "hd": "40GB"
    }
    }

    View Slide

  39. Esquema para los productos II
    {
    "type": "book", #obligatorio
    "title": "House of Leaves", #obligatorio
    "stock": 850, #obligatorio
    "price": 22.85, #obligatorio
    "author": "Mark Z. Danielewski",
    "pages": 736,
    "isbn": "9780375410345",
    "book_type": "hardcover"
    }

    View Slide

  40. Esquema para los carros
    {
    "session": "__SESSION_ID_039210321", #obligatorio
    "status": "active", #auto generado
    "products": [ # documento embebido
    {
    "_id": "832013821083210", #obligatorio
    "quantity": 1, #obligatorio
    "title": "Sukkwan Island"
    },
    {
    "_id": "123421432809843", #obligatorio
    "quantity": 2, #obligatorio
    "title": "The Grapes of Wrath"
    }
    ]
    }

    View Slide

  41. Gestión del catálogo
     [GET] /catalog
    Obtiene todos los productos del catálogo.
     [GET] /catalog?type=
    Obtiene todos los productos del catálogo de un tipo.
     [GET] /catalog/
    Obtiene un producto.
     [PUT] /catalog
    Añade un producto.
     [POST] /catalog/
    Edita (reemplaza) los datos de un producto.
     [DELETE] /catalog/
    Borra un producto.

    View Slide

  42. Gestión de los carritos I
     [GET] /cart
    Obtiene todos los carros.
     [GET] /cart/
    Obtiene un carro.
     [PUT] /cart
    Añade un carro.
     [POST] /cart/
    Edita (reemplaza) los datos de un carro.
     [DELETE] /catalog/
    Borra un carro.

    View Slide

  43. Gestión de los carros II
     [PUT] /cart//product
    Añade un producto al carros.
     [DELETE] /cart/
    Borra un producto de un carros.
    BOLA EXTRA
     [GET] /platform/
    Devuelve las consolas disponibles y el número de
    juegos disponibles.

    View Slide

  44. A tener en cuenta
     Al añadir un producto a un carro hay que
    actualizar el stock.
     Al borrar un producto de un carro hay que
    actualizar el stock.
     Al borrar/actualizar un producto hay que
    borrarlo de los carros.
     Al añadir/editar algo hay que devolver el
    objeto añadido/modificado.
     El código debe ser lo más atómico posible.
     ¡Usad índices!

    View Slide

  45. View Slide

  46. Plantilla
    $ sudo apt-get install git python-dev python-pip
    $ sudo pip install pymongo Flask
    $ git clone https://github.com/csuarez/juc-mongodb-api-
    template.git
    $ cd juc-mongodb-api-template
    $ mongoimport --db shop --collection catalog --file catalog.json
    --host 0.0.0.0
    $ python api.py
    @app.route('/catalog/', methods=['GET'])
    def catalog_get_single(id):
    product = catalog_col.find_one({'_id': ObjectId(id)})
    if product == None:
    abort(404)
    return jsonify(product)
    Ejemplo
    https://github.com/csuarez/juc-mongodb-api-template

    View Slide

  47. View Slide