Share
Embed
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
Transcript
47 slides
-
CONSTRUYENDO UNA API REST CON PYTHON Y MONGODB César Suárez
Ortega <[email protected]> II Jornadas Técnicas UEx – CIEMAT: Introducción a NoSQL con MongoDB 9 – 11 de Febrero, 2016 -
Índice Qué es una API REST Python + MongoDB =
PyMongo Práctica: API para una tienda online -
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 -
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. -
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. -
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/ -
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)" } ] } -
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 -
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) -
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} ) -
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'] -
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'): ... -
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']} ]) -
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 } ] -
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'] -
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"]}) -
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 ) -
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}) -
API REST para una tienda online Gestión del catalógo y
su stock Gestión de los carritos -
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" } } -
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" } -
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" } ] } -
Gestión del catálogo [GET] /catalog Obtiene todos los productos del
catálogo. [GET] /catalog?type=<type_name> Obtiene todos los productos del catálogo de un tipo. [GET] /catalog/<objectid> Obtiene un producto. [PUT] /catalog Añade un producto. [POST] /catalog/<objectid> Edita (reemplaza) los datos de un producto. [DELETE] /catalog/<objectid> Borra un producto. -
Gestión de los carritos I [GET] /cart Obtiene todos los
carros. [GET] /cart/<objectid> Obtiene un carro. [PUT] /cart Añade un carro. [POST] /cart/<objectid> Edita (reemplaza) los datos de un carro. [DELETE] /catalog/<objectid> Borra un carro. -
Gestión de los carros II [PUT] /cart/<cart_id>/product Añade un producto
al carros. [DELETE] /cart/<objectid> Borra un producto de un carros. BOLA EXTRA [GET] /platform/ Devuelve las consolas disponibles y el número de juegos disponibles. -
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! -
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/<id>', 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