Cuando se trata de desarrollo backend con Python, la elección de la base de datos es una de las decisiones más importantes del proyecto. MongoDB, la base de datos NoSQL más popular del mundo, se ha convertido en la opción preferida para aplicaciones que exigen flexibilidad, escalabilidad y rendimiento. En esta guía completa, aprenderás todo sobre cómo integrar Python con MongoDB usando el driver oficial PyMongo, desde la instalación hasta un proyecto práctico completo.
¿Qué es MongoDB?
MongoDB es una base de datos NoSQL orientada a documentos. A diferencia de las bases de datos relacionales tradicionales como MySQL o PostgreSQL, que almacenan datos en tablas con filas y columnas fijas, MongoDB almacena datos en documentos flexibles en formato BSON (una extensión binaria de JSON). Cada documento puede tener una estructura diferente, lo que hace de MongoDB la opción ideal para aplicaciones con esquemas dinámicos, prototipado rápido e integración con datos de fuentes variadas.
Entre los casos de uso más comunes de MongoDB se encuentran: sistemas de catálogo de productos, plataformas de comercio electrónico, sistemas de gestión de contenido, aplicaciones IoT, análisis de datos en tiempo real y almacenamiento de logs. Grandes empresas como Forbes, Uber y EA Sports utilizan MongoDB en producción. Puedes consultar más detalles en la documentación oficial de MongoDB.
¿Por qué usar Python con MongoDB?
La combinación Python + MongoDB es natural y poderosa por varias razones. Primero, la estructura de documentos de MongoDB se asemeja a los diccionarios de Python, lo que hace que la conversión entre ambos sea prácticamente automática. Segundo, el driver PyMongo es maduro, bien documentado y sigue las mejores prácticas del lenguaje. Tercero, la flexibilidad del esquema de MongoDB permite evolucionar tu aplicación sin necesidad de migrar bases de datos enteras.
Además, el ecosistema Python ofrece bibliotecas complementarias que se integran perfectamente con MongoDB, como pandas para análisis de datos y Flask o FastAPI para la construcción de APIs. Si estás empezando con Python, te recomiendo consultar nuestra guía sobre gestión de dependencias con pip antes de continuar.
Instalación y Configuración
Instalando MongoDB
Antes de escribir cualquier código, necesitas tener MongoDB instalado. Existen dos formas principales: instalarlo localmente o usar una instancia en la nube con MongoDB Atlas. Para desarrollo local, visita la página de descarga de MongoDB Community Server y elige la versión para tu sistema operativo. En Windows, el instalador incluye MongoDB como servicio de Windows. En Linux, puedes instalarlo mediante el gestor de paquetes. En macOS, Homebrew facilita la instalación con brew install mongodb-community.
Si prefieres no instalar nada localmente, MongoDB Atlas ofrece un nivel gratuito (M0) con 512 MB de almacenamiento, perfecto para aprendizaje y proyectos personales. Solo necesitas crear una cuenta, configurar un clúster gratuito y obtener tu cadena de conexión.
Instalando PyMongo
Con Python y MongoDB listos, instala el driver oficial PyMongo usando pip:
pip install pymongo
Para instalar con soporte de compresión y cifrado TLS/SSL, usa:
pip install pymongo[snappy,gssapi,srv,tls]
La biblioteca está disponible en PyPI, y la documentación completa se encuentra en la página oficial de PyMongo.
Verificando la Instalación
Para confirmar que todo funciona, abre un terminal de Python y ejecuta:
import pymongo
print(pymongo.__version__)
Si devuelve la versión instalada (ej: 4.9.1), la instalación fue exitosa.
Conectando a MongoDB
La conexión a MongoDB se realiza mediante la clase MongoClient. La cadena de conexión varía según el entorno:
from pymongo import MongoClient
Conexión local (por defecto)
cliente = MongoClient("mongodb://localhost:27017/")
Conexión con MongoDB Atlas
cliente = MongoClient("mongodb+srv://usuario:contraseñ[email protected]/")
Accediendo a una base de datos
db = cliente["mi_base_datos"]
Accediendo a una colección
coleccion = db["mi_coleccion"]
MongoDB es perezoso: la conexión solo se establece cuando se ejecuta la primera operación. El objeto MongoClient gestiona automáticamente el pool de conexiones, por lo que debes crear una única instancia global y reutilizarla en toda la aplicación, en lugar de crear una nueva conexión para cada operación.
Es importante manejar las excepciones de conexión. Usa try/except para capturar pymongo.errors.ConnectionFailure y pymongo.errors.ServerSelectionTimeoutError:
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
try:
cliente.admin.command("ping")
print("¡Conectado a MongoDB!")
except (ConnectionFailure, ServerSelectionTimeoutError) as e:
print(f"Error de conexión: {e}")
Operaciones CRUD con PyMongo
Las operaciones CRUD (Create, Read, Update, Delete) son la base de cualquier aplicación que interactúa con una base de datos. Vamos a explorar cada una con ejemplos prácticos.
Create: Insertando Documentos
Para insertar un documento, usa los métodos insert_one o insert_many:
# Insertando un solo documento
usuario = {
"nombre": "Ana Silva",
"email": "[email protected]",
"edad": 28,
"habilidades": ["Python", "MongoDB", "FastAPI"],
"activo": True
}
resultado = coleccion.insert_one(usuario)
print(f"ID del documento insertado: {resultado.inserted_id}")
Insertando múltiples documentos
usuarios = [
{"nombre": "Carlos", "email": "[email protected]", "edad": 35},
{"nombre": "María", "email": "[email protected]", "edad": 42},
{"nombre": "Juan", "email": "[email protected]", "edad": 25}
]
resultados = coleccion.insert_many(usuarios)
print(f"IDs insertados: {resultados.inserted_ids}")
Si no especificas un campo _id, MongoDB genera automáticamente un ObjectId único. También puedes definir tu propio _id si necesitas un identificador personalizado.
Read: Consultando Documentos
Para consultar datos, usa find_one (devuelve un documento) o find (devuelve un cursor iterable):
# Buscar un documento por campo
usuario = coleccion.find_one({"email": "[email protected]"})
print(usuario["nombre"])
Buscar todos los documentos con filtro
usuarios_activos = coleccion.find({"activo": True})
for usuario in usuarios_activos:
print(usuario["nombre"])
Proyección: seleccionar campos específicos
usuarios = coleccion.find(
{"edad": {"$gte": 30}},
{"nombre": 1, "email": 1, "_id": 0}
)
Ordenación
usuarios_ordenados = coleccion.find().sort("nombre", 1) # 1 ascendente, -1 descendente
Limitar y saltar resultados (paginación)
pagina = coleccion.find().sort("nombre", 1).skip(0).limit(10)
MongoDB ofrece una amplia gama de operadores de consulta como $gt, $lt, $in, $regex, $exists y muchos más. Consulta la guía de aggregation de MongoDB para consultas más avanzadas.
Update: Actualizando Documentos
Para actualizar documentos, usa update_one o update_many:
# Actualizar un documento específico
coleccion.update_one(
{"email": "[email protected]"},
{"$set": {"edad": 29, "activo": True}}
)
Incrementar un valor numérico
coleccion.update_one(
{"nombre": "Carlos"},
{"$inc": {"edad": 1}}
)
Añadir un elemento a un array
coleccion.update_one(
{"nombre": "Ana Silva"},
{"$push": {"habilidades": "Docker"}}
)
Actualizar múltiples documentos
coleccion.update_many(
{"activo": False},
{"$set": {"activo": True}}
)
Los operadores de actualización como $set, $unset, $inc, $push, $pull y $addToSet permiten manipular documentos de forma precisa sin tener que reenviar el documento completo.
Delete: Eliminando Documentos
Para eliminar documentos, usa delete_one o delete_many:
# Eliminar un documento
coleccion.delete_one({"email": "[email protected]"})
Eliminar múltiples documentos
coleccion.delete_many({"activo": False})
Eliminar todos los documentos de una colección (¡cuidado!)
coleccion.delete_many({})
Eliminar la colección completa
coleccion.drop()
Aggregation Pipeline
El Aggregation Pipeline es una de las características más potentes de MongoDB. Permite procesar documentos en etapas secuenciales, similar al pipeline de comandos en Unix. Cada etapa transforma los documentos de entrada y pasa el resultado a la siguiente etapa.
pipeline = [
{"$match": {"edad": {"$gte": 25}}},
{"$group": {
"_id": "$ciudad",
"total": {"$sum": 1},
"edad_promedio": {"$avg": "$edad"}
}},
{"$sort": {"total": -1}},
{"$limit": 5}
]
resultados = coleccion.aggregate(pipeline)
for resultado in resultados:
print(f"Ciudad: {resultado['_id']}, Total: {resultado['total']}")
Las etapas más comunes incluyen $match (filtrado), $group (agrupación), $sort (ordenación), $project (proyección/transformación), $lookup (join entre colecciones) y $unwind (desconstrucción de arrays). Dominar el aggregation pipeline es esencial para extraer información avanzada de tus datos.
Índices y Rendimiento
Los índices son fundamentales para mantener el rendimiento de la base de datos a medida que los datos crecen. Sin índices, MongoDB necesita escanear todos los documentos de una colección (collection scan) para encontrar los documentos, lo cual se vuelve inviable en colecciones con millones de documentos.
# Crear un índice simple en un campo
coleccion.create_index("email")
Crear un índice compuesto
coleccion.create_index([("ciudad", 1), ("edad", -1)])
Crear un índice único
coleccion.create_index("email", unique=True)
Crear un índice de texto para búsqueda textual
coleccion.create_index([("nombre", "text"), ("biografia", "text")])
Listar todos los índices de la colección
for indice in coleccion.list_indexes():
print(indice)
Eliminar un índice
coleccion.drop_index("email_1")
Usa el método explain() para analizar cómo MongoDB está ejecutando tus consultas y verificar si los índices se están utilizando. La documentación oficial de índices de MongoDB explica en detalle cada tipo de índice disponible.
Buenas Prácticas con PyMongo
1. Pool de Conexiones
Crea una única instancia de MongoClient y reutilízala en toda la aplicación. El cliente gestiona internamente un pool de conexiones. Configura maxPoolSize y minPoolSize según la carga esperada de tu aplicación.
2. Manejo de Excepciones
Siempre encapsula las operaciones con la base de datos en bloques try/except. Además de las excepciones de conexión, captura DuplicateKeyError para violaciones de índice único y BulkWriteError para operaciones por lote.
3. Proyección de Campos
Utiliza siempre proyección para devolver solo los campos necesarios. Esto reduce la cantidad de datos transferidos por la red y mejora el rendimiento de las consultas.
# En lugar de traer el documento completo...
usuario = coleccion.find_one({"email": "[email protected]"})
...proyecta solo los campos necesarios
usuario = coleccion.find_one(
{"email": "[email protected]"},
{"nombre": 1, "email": 1}
)
4. Usa bulk_write para Operaciones por Lote
Cuando necesites insertar, actualizar o eliminar muchos documentos, prefiere operaciones por lote con bulk_write en lugar de llamadas individuales dentro de un bucle. Esto reduce drásticamente el número de viajes a la base de datos.
5. Índices Adecuados
Analiza tus consultas más frecuentes y crea índices que las cubran. Usa explain() para identificar consultas lentas y MongoDB Compass para monitorear el rendimiento visualmente.
Modelado de Datos con MongoDB
Una de las decisiones más importantes al trabajar con MongoDB es cómo modelar tus datos. A diferencia de las bases de datos relacionales, donde la normalización es la regla, MongoDB fomenta la incorporación (embedding) de documentos relacionados dentro de un solo documento siempre que sea posible. Esto reduce la necesidad de joins y mejora el rendimiento de lectura.
Por ejemplo, en un sistema de comercio electrónico, en lugar de tener tablas separadas de pedidos y artículos, puedes incorporar los artículos directamente en el documento del pedido:
pedido = {
"_id": ObjectId(),
"cliente": "María Souza",
"fecha": datetime.now(),
"total": 250.00,
"articulos": [
{"producto": "Teclado Mecánico", "cantidad": 1, "precio": 200.00},
{"producto": "Alfombrilla", "cantidad": 1, "precio": 50.00}
],
"direccion_envio": {
"calle": "Av. Paulista",
"numero": 1000,
"ciudad": "São Paulo",
"codigo_postal": "01310-100"
}
}
La regla general es: incorpora cuando la relación sea del tipo "contiene" (uno-pocos) y referencia cuando sea muchos-a-muchos o cuando los datos relacionados sean grandes y se acceda a ellos de forma independiente. MongoDB también soporta $lookup para hacer joins entre colecciones cuando sea necesario.
Transacciones y Atomicidad
Aunque MongoDB es conocido por su flexibilidad, también soporta transacciones multi-documento para escenarios que requieren atomicidad. Una transacción garantiza que múltiples operaciones se ejecuten como una sola unidad: o todas se aplican, o ninguna. Esto es esencial en sistemas financieros, reservas y cualquier aplicación que exija consistencia rigurosa.
Con PyMongo, puedes usar transacciones en réplicas o clústeres sharded a partir de MongoDB 4.0:
from pymongo import MongoClient
cliente = MongoClient()
db = cliente["banco"]
cuentas = db["cuentas"]
transacciones = db["transacciones"]
with cliente.start_session() as sesion:
with sesion.start_transaction():
cuentas.update_one(
{"cuenta": "A"}, {"$inc": {"saldo": -100}}, session=sesion
)
cuentas.update_one(
{"cuenta": "B"}, {"$inc": {"saldo": 100}}, session=sesion
)
transacciones.insert_one({
"origen": "A", "destino": "B", "valor": 100
}, session=sesion)
Si alguna operación falla, todas se revierten automáticamente
Las transacciones deben usarse con moderación, ya que tienen un costo de rendimiento. Prefiere operaciones atómicas en un solo documento siempre que sea posible. MongoDB garantiza atomicidad a nivel de documento por defecto, lo que cubre la mayoría de los casos de uso.
Herramientas del Ecosistema MongoDB
El ecosistema MongoDB ofrece diversas herramientas que facilitan el desarrollo y la administración de la base de datos. MongoDB Compass es la interfaz gráfica oficial que permite visualizar y manipular datos, crear índices y analizar el rendimiento de consultas visualmente. MongoDB Shell (mongosh) es el terminal interactivo para administración avanzada. Para desarrollo local, mongod es el servidor de base de datos, mientras que mongos gestiona clústeres sharded en producción.
Para proyectos más grandes, MongoDB Charts permite crear visualizaciones de datos directamente desde la base de datos, y MongoDB Realm ofrece sincronización en tiempo real para aplicaciones móviles. En entornos de producción, es esencial configurar monitoreo con MongoDB Ops Manager o Atlas Monitoring para seguir métricas como uso de memoria, operaciones por segundo y latencia de consultas.
Además de PyMongo, existen otras bibliotecas Python que abstraen la interacción con MongoDB. MongoEngine es un ODM (Object Document Mapper) que funciona de forma similar a un ORM, permitiendo definir esquemas con clases Python. Beanie es un ODM asíncrono basado en Pydantic que se integra perfectamente con FastAPI. Para análisis de datos, pandas puede leer directamente de colecciones MongoDB usando pd.DataFrame(list(coleccion.find())).
# Ejemplo de integración MongoDB con pandas
import pandas as pd
from pymongo import MongoClient
cliente = MongoClient()
db = cliente["ventas"]
coleccion = db["pedidos"]
Cargar datos de MongoDB a un DataFrame
df = pd.DataFrame(list(coleccion.find({"estado": "entregado"})))
print(f"Total de ventas: ${df['total'].sum():.2f}")
print(f"Promedio por pedido: ${df['total'].mean():.2f}")
Proyecto Práctico: Sistema de Biblioteca
Vamos a consolidar todo lo aprendido con un proyecto práctico: un sistema simple de gestión de biblioteca con operaciones para añadir libros, registrar préstamos y generar informes.
import pymongo
from pymongo import MongoClient
from datetime import datetime, timedelta
class Biblioteca:
def init(self):
self.cliente = MongoClient("mongodb://localhost:27017/")
self.db = self.cliente["biblioteca"]
self.libros = self.db["libros"]
self.prestamos = self.db["prestamos"]
self._crear_indices()
def _crear_indices(self):
self.libros.create_index("isbn", unique=True)
self.libros.create_index([("titulo", "text"), ("autor", "text")])
self.prestamos.create_index("libro_id")
def agregar_libro(self, titulo, autor, isbn, año, copias=1):
libro = {
"titulo": titulo,
"autor": autor,
"isbn": isbn,
"año": año,
"copias_disponibles": copias,
"copias_total": copias
}
try:
self.libros.insert_one(libro)
return f"Libro '{titulo}' agregado correctamente."
except pymongo.errors.DuplicateKeyError:
return f"ISBN {isbn} ya existe en el sistema."
def registrar_prestamo(self, isbn, usuario):
libro = self.libros.find_one({"isbn": isbn})
if not libro or libro["copias_disponibles"] <= 0:
return "Libro no disponible para préstamo."
prestamo = {
"libro_id": libro["_id"],
"usuario": usuario,
"fecha_prestamo": datetime.now(),
"fecha_devolucion": datetime.now() + timedelta(days=14),
"devuelto": False
}
self.prestamos.insert_one(prestamo)
self.libros.update_one(
{"_id": libro["_id"]},
{"$inc": {"copias_disponibles": -1}}
)
return f"Préstamo registrado para {usuario}. Devolución en 14 días."
def libros_mas_prestados(self, limite=5):
pipeline = [
{"$group": {
"_id": "$libro_id",
"total_prestamos": {"$sum": 1}
}},
{"$sort": {"total_prestamos": -1}},
{"$limit": limite},
{"$lookup": {
"from": "libros",
"localField": "_id",
"foreignField": "_id",
"as": "libro"
}},
{"$unwind": "$libro"},
{"$project": {
"titulo": "$libro.titulo",
"autor": "$libro.autor",
"total_prestamos": 1
}}
]
return list(self.prestamos.aggregate(pipeline))
Ejemplo de uso
biblio = Biblioteca()
biblio.agregar_libro("Python para Datos", "Wes McKinney", "978-1-449-35901-7", 2023, 3)
biblio.agregar_libro("MongoDB: The Definitive Guide", "Kristina Chodorow", "978-1-449-34480-9", 2022, 2)
print(biblio.registrar_prestamo("978-1-449-35901-7", "Carlos"))
print(biblio.libros_mas_prestados())
Conclusión
La integración entre Python y MongoDB con PyMongo es una combinación potente y versátil para construir aplicaciones modernas. En esta guía, aprendiste desde la instalación hasta un proyecto completo, pasando por operaciones CRUD, aggregation pipeline, índices y buenas prácticas. El ecosistema MongoDB es vasto y ofrece características como Change Streams, Transacciones y GridFS para archivos grandes. Para profundizar, te recomiendo realizar los cursos gratuitos de MongoDB University y seguir el blog oficial de MongoDB.
Si aún no has configurado tu entorno Python, consulta nuestro tutorial sobre entornos virtuales con venv para mantener tus dependencias organizadas. Con estos conocimientos, ¡estás listo para construir aplicaciones Python escalables y flexibles con MongoDB!