0. 🎯 Introducción - ¿Qué vamos a lograr?
🎓 LO QUE APRENDERÁS EN ESTA GUÍA (Sistema Híbrido 2026):
Crear un sistema de biblioteca que usa MySQL (local) y MongoDB Atlas (nube) trabajando juntos mediante un campo común.
🗄️ MySQL (Local)
- Usuarios: auth_user (Django)
- Préstamos: biblioteca_prestamo
- Multas: biblioteca_multa
- Campo clave: libro_id (entero)
- ¿Por qué? Transacciones ACID, relaciones FK
☁️ MongoDB Atlas (Nube)
- Catálogo: libros (colección)
- Metadatos: autor, categorías, reseñas
- Logs: actividad del sistema
- Campo clave: libro_id (entero común)
- ¿Por qué? Esquema flexible, escalable
🔗 INTEGRACIÓN: Campo Común libro_id
Ejemplo de consulta híbrida:
# 1. Django obtiene préstamo de MySQL
prestamo = Prestamo.objects.get(id=1)
libro_id = prestamo.libro_id # ← Campo común (ej: 5)
# 2. Django busca detalles del libro en MongoDB Atlas
libro_mongo = db.libros.find_one({"libro_id": libro_id})
# 3. Combina datos de ambas BDs
resultado = {
"usuario": prestamo.usuario.username, # MySQL
"libro": libro_mongo["titulo"], # MongoDB
"autor": libro_mongo["autor"]["nombre"], # MongoDB
"fecha_prestamo": prestamo.fecha # MySQL
}
🏆 Sistema Híbrido: Lo Mejor de Ambos Mundos
Este proyecto combina MySQL local (datos relacionales) con MongoDB Atlas (datos flexibles) usando un campo común: libro_id
| Aspecto | MySQL (Local) | MongoDB Atlas (Nube) |
|---|---|---|
| ¿Qué guarda? | Usuarios, préstamos, multas, transacciones | Catálogo de libros, reseñas, metadatos, logs |
| ¿Por qué? | ACID, integridad referencial, transacciones críticas | Esquema flexible, escalabilidad, datos no estructurados |
| Campo común | libro_id (INT) en tabla prestamos | libro_id (INT) en documento libros |
| Tecnología | Django ORM (models.py) | PyMongo (consultas directas) |
| Ejemplo de datos |
Prestamo(id=1, libro_id=5, usuario_id=10)
|
{"libro_id": 5, "titulo": "Cien Años..."}
|
🔗 Flujo de Integración:
- Usuario pide un libro prestado → Django crea registro en MySQL (tabla prestamos)
- Sistema necesita mostrar datos del libro → Django consulta MongoDB Atlas usando el libro_id
- Se combinan datos:
- Fecha de préstamo (MySQL)
- Título, autor, categorías (MongoDB)
- Usuario devuelve el libro → Django actualiza MySQL
- Se registra log de actividad → Django inserta en MongoDB Atlas
⚠️ IMPORTANTE - MongoDB Atlas Versión Gratuita 2026:
Esta guía está actualizada para las limitaciones actuales de Atlas:
- 🔴 1 solo cluster M0 gratuito por cuenta
- ✅ Múltiples bases de datos dentro del cluster (ilimitadas)
- ✅ Múltiples colecciones por base de datos (ilimitadas)
- 💾 512 MB de almacenamiento total compartido entre todas las BDs
- ⏸️ Pausa automática después de 60 días de inactividad
📖 Esta guía te enseñará:
- Cómo crear tu único cluster gratuito
- Cómo organizar múltiples bases de datos dentro de él
- Cómo conectar Django a diferentes BDs del mismo cluster
- Cómo monitorear el uso de almacenamiento (max 512 MB)
🎬 Flujo de Trabajo Completo - De Principio a Fin:
- Crear cuenta en MongoDB Atlas
- Registrarse con Google o email
- Verificar cuenta
- Crear organización y proyecto
- Configurar cluster gratuito M0
- Seleccionar plan FREE (512 MB)
- Elegir región (AWS us-east-1)
- Nombrar cluster:
BibliotecaCluster
- Configurar seguridad
- Crear usuario de BD:
biblioteca_admin - Agregar IP a whitelist:
0.0.0.0/0(desarrollo)
- Crear usuario de BD:
- Obtener connection string
- Copiar URL de conexión
- Reemplazar contraseña
- Agregar nombre de base de datos
- Instalar dependencias Python
pip install pymongo dnspython
- Conectar Django a MongoDB
- Configurar
settings.py - Crear servicio
MongoService - Probar conexión con script de test
- Configurar
- Crear bases de datos en el cluster
biblioteca_catalogo→ libros, autoresbiblioteca_logs→ actividad, errores
- Implementar CRUD en Django
- Views para crear, listar, editar, eliminar
- Templates HTML con formularios
- Validaciones y manejo de errores
- Probar y verificar en Atlas
- Insertar documentos de prueba
- Ver en Atlas → Browse Collections
- Monitorear uso de almacenamiento
⚙️ ¿Qué Hace Cada Tecnología en Este Sistema Híbrido?
| Componente | Función Específica | ¿Por qué lo usamos? |
|---|---|---|
| MySQL (Local) | Base de datos relacional para préstamos, usuarios, multas | ACID, transacciones críticas, integridad referencial con Foreign Keys |
| Django ORM | Interfaz Python para MySQL (models.py) | Define modelos: User, Prestamo, Multa con relaciones y validaciones |
| MongoDB Atlas | Base de datos NoSQL en la nube para catálogo y logs | Esquema flexible, 512 MB gratis, escalable, sin instalación local |
| Cluster M0 | Servidor virtual gratuito que contiene todas las BDs de MongoDB | Única opción gratuita, incluye replicación y backups automáticos |
| PyMongo | Driver Python oficial para conectar Django con MongoDB | API pythónica, operaciones CRUD directas, soporte MongoDB 7.x |
| Django | Framework web que orquesta MySQL y MongoDB | Usa ORM para MySQL y PyMongo para MongoDB en paralelo |
| Campo Común (libro_id) | Entero que identifica el mismo libro en ambas BDs | Permite relacionar préstamos (MySQL) con detalles del libro (MongoDB) |
| Connection String | URL de conexión a MongoDB Atlas | Formato: mongodb+srv://user:pass@cluster.mongodb.net/database |
🔗 Ejemplo de Interacción MySQL ↔ MongoDB:
from django.shortcuts import render
from .models import Prestamo # Modelo Django → MySQL
from django.conf import settings # Para acceder a MongoDB
def detalle_prestamo(request, prestamo_id):
# 1. Obtener préstamo de MySQL (Django ORM)
prestamo = Prestamo.objects.get(id=prestamo_id)
libro_id = prestamo.libro_id # ← Campo común (ej: 5)
# 2. Obtener detalles del libro de MongoDB (PyMongo)
mongo_db = settings.MONGO_CLIENT['biblioteca_catalogo']
libro = mongo_db.libros.find_one({"libro_id": libro_id})
# 3. Combinar datos de ambas bases de datos
context = {
'usuario': prestamo.usuario.username, # MySQL
'fecha_prestamo': prestamo.fecha, # MySQL
'titulo': libro['titulo'], # MongoDB
'autor': libro['autor']['nombre'], # MongoDB
'isbn': libro['isbn'], # MongoDB
'categorias': libro['categorias'] # MongoDB
}
return render(request, 'detalle.html', context)
⚙️ Requisitos Previos (VERIFICAR ANTES DE COMENZAR)
Asegúrate de tener TODO listo antes de empezar:
1. Python 3.8 o superior
¿Cómo verificar?
python --version
Debe mostrar: Python 3.8.x, 3.9.x, 3.10.x o superior
❌ Si no está instalado: Descarga desde python.org/downloads
2. Cuenta de MongoDB Atlas (Gratuita)
- ✅ Email verificado (Gmail, Outlook, etc.)
- ✅ Conexión a Internet estable
- ✅ Navegador moderno (Chrome, Firefox, Edge)
3. Editor de Código
Recomendado: Visual Studio Code (gratis)
4. Conocimientos Básicos
- 💡 Python básico (variables, funciones, clases)
- 💡 JSON (formato de datos)
- 💡 Línea de comandos básica (cd, pip, python)
- 💡 Django básico (opcional pero recomendado)
📅 Tiempo estimado total: 2-3 horas (primera vez)
🎯 Nivel de dificultad: Intermedio
📦 Dependencias a instalar: 4 paquetes Python
📊 Arquitectura del Sistema Híbrido (MySQL + MongoDB Atlas)
┌─────────────────────────────────────────────────────────────────────┐
│ NAVEGADOR WEB (Cliente) │
│ - HTML/CSS/JavaScript/Bootstrap │
│ - Peticiones HTTP │
└──────────────────────┬──────────────────────────────────────────────┘
│
│ GET /prestamos/
│ POST /prestar-libro/
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ SERVIDOR DJANGO (Backend - Orquestador) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ views.py (Lógica de Negocio) │ │
│ │ │ │
│ │ def prestar_libro(request): │ │
│ │ # 1. Buscar libro en MongoDB │ │
│ │ libro = mongo_db.libros.find_one({"libro_id": 5}) │ │
│ │ │ │
│ │ # 2. Crear préstamo en MySQL │ │
│ │ prestamo = Prestamo.objects.create( │ │
│ │ libro_id=5, ← CAMPO COMÚN │ │
│ │ usuario=request.user │ │
│ │ ) │ │
│ │ │ │
│ │ # 3. Combinar datos para respuesta │ │
│ │ return render(request, 'prestamo.html', { │ │
│ │ 'libro': libro, # MongoDB │ │
│ │ 'prestamo': prestamo # MySQL │ │
│ │ }) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ models.py │ │ services/mongo.py │ │
│ │ (Django ORM) │ │ (PyMongo) │ │
│ │ │ │ │ │
│ │ class Prestamo │ │ client = MongoClient() │ │
│ │ class Usuario │ │ db = client['biblioteca']│ │
│ │ class Multa │ │ libros = db.libros │ │
│ └────────┬─────────┘ └──────────┬───────────────┘ │
│ │ │ │
└───────────┼───────────────────────────────────┼────────────────────┘
│ │
│ Django ORM │ PyMongo Driver
│ SQL Queries │ BSON/JSON
│ │
▼ ▼
┌──────────────────────────┐ ┌───────────────────────────────────┐
│ MYSQL (Local/Xampp) │ │ MONGODB ATLAS (Nube) │
│ │ │ │
│ 📁 biblioteca_db │ │ ☁️ BibliotecaCluster (M0 Free) │
│ │ │ │
│ 📋 Tablas: │ │ 📁 biblioteca_catalogo (BD) │
│ ┌─────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ auth_user │ │ │ │ libros (Colección) │ │
│ │ ├─ id │ │ │ │ { │ │
│ │ ├─ username │ │ │ │ libro_id: 5, ← COMÚN │ │
│ │ └─ email │ │ │ │ titulo: "Cien Años...",│ │
│ └─────────────────┘ │ │ │ isbn: "978-0307...", │ │
│ │ │ │ autor: { │ │
│ ┌─────────────────┐ │ │ │ nombre: "García...", │ │
│ │ biblioteca_prest│ │ │ │ pais: "Colombia" │ │
│ │ ├─ id │ │ │ │ }, │ │
│ │ ├─ libro_id ◄──┼────┼──────┼──┼─▶ categorias: [...], │ │
│ │ ├─ usuario_id │ │ │ │ stock: 10 │ │
│ │ ├─ fecha_prest │ │ │ │ } │ │
│ │ └─ fecha_devol │ │ │ └──────────────────────────┘ │
│ └─────────────────┘ │ │ │
│ │ │ 📁 biblioteca_logs (BD) │
│ ┌─────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ biblioteca_multa│ │ │ │ logs_actividad │ │
│ │ ├─ id │ │ │ │ { │ │
│ │ ├─ prestamo_id │ │ │ │ usuario: "admin", │ │
│ │ ├─ monto │ │ │ │ accion: "PRESTAMO", │ │
│ │ └─ pagada │ │ │ │ libro_id: 5, │ │
│ └─────────────────┘ │ │ │ timestamp: ISODate() │ │
│ │ │ │ } │ │
│ ⚙️ ACID Transactions │ │ └──────────────────────────┘ │
│ 🔗 Foreign Keys │ │ │
│ 📊 Relaciones 1-N │ │ 📁 biblioteca_estadisticas (BD) │
└──────────────────────────┘ │ ┌──────────────────────────┐ │
│ │ visitas │ │
│ │ { │ │
│ │ fecha: "2026-01-30", │ │
│ │ total_visitas: 120, │ │
│ │ libros_prestados: 15 │ │
│ │ } │ │
│ └──────────────────────────┘ │
│ │
│ 💾 512 MB total │
│ ✨ Esquema Flexible │
│ 🔄 Replicación Automática │
└───────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════
FLUJO DE DATOS - PRESTAR LIBRO:
═══════════════════════════════════════════════════════════════════════
1. 📝 Usuario selecciona libro "Cien Años de Soledad" (libro_id=5)
2. 🔍 Django consulta MongoDB Atlas:
libro = db.libros.find_one({"libro_id": 5})
→ Obtiene: {titulo, autor, stock, categorias}
3. ✅ Django verifica stock disponible (MongoDB)
if libro["stock"] > 0:
4. 💾 Django crea préstamo en MySQL:
Prestamo.objects.create(
libro_id=5, ← Campo común
usuario=request.user,
fecha_prestamo=datetime.now()
)
5. 🔄 Django actualiza stock en MongoDB:
db.libros.update_one(
{"libro_id": 5},
{"$inc": {"stock": -1}}
)
6. 📊 Django registra log en MongoDB:
db.logs_actividad.insert_one({
"accion": "PRESTAMO",
"libro_id": 5,
"usuario": request.user.username,
"timestamp": datetime.now()
})
7. ✨ Django combina datos y muestra:
- Nombre usuario (MySQL)
- Título del libro (MongoDB)
- Fecha préstamo (MySQL)
- Autor del libro (MongoDB)
═══════════════════════════════════════════════════════════════════════
¿POR QUÉ ESTE DISEÑO HÍBRIDO?
═══════════════════════════════════════════════════════════════════════
✅ MySQL: Préstamos necesitan ACID (si falla préstamo, hacer rollback)
✅ MongoDB: Libros tienen datos flexibles (algunos tienen ebook, otros no)
✅ Campo común (libro_id): Permite unir datos de ambas BDs
✅ Escalabilidad: Catálogo crece sin afectar transacciones críticas
⏱️ Tiempo Estimado Total: 2-3 horas
| Fase | Tiempo | Qué harás |
|---|---|---|
| PASO 1: MongoDB Atlas | 20 min | Crear cuenta, cluster gratuito, usuario de BD, whitelist IPs |
| PASO 2: Proyecto Django | 15 min | Crear proyecto, instalar PyMongo y dnspython |
| PASO 3: Conexión MongoDB | 15 min | Configurar connection string, probar conexión, crear primera BD |
| PASO 4: Organizar Bases de Datos | 20 min | Crear múltiples BDs en el cluster, configurar acceso en settings.py |
| PASO 5: Operaciones CRUD | 35 min | insert_one, find, update_one, delete_one, queries avanzadas |
| PASO 6: Views Django | 25 min | Crear views para listar, crear, editar y eliminar documentos |
| PASO 7: Templates HTML | 20 min | Formularios y listados con Bootstrap 5 |
| PASO 8: Pruebas y Depuración | 20 min | Verificar CRUD completo, monitorear uso en Atlas |
🔄 ¿Por Qué Sistema Híbrido MySQL + MongoDB?
Este diseño aprovecha las fortalezas de cada base de datos:
| Usa MySQL Para... | Usa MongoDB Para... | Conecta Con... |
|---|---|---|
| Préstamos (Prestamo model) | Catálogo de libros (colección libros) |
Campo común: libro_id (INT) MySQL: prestamo.libro_id ↕️ MongoDB: libro.libro_id |
| Usuarios (auth_user) | Reseñas (subdocumentos flexibles) | |
| Multas (Multa model) | Logs de actividad (eventos del sistema) |
✅ Ventajas del Sistema Híbrido:
- MySQL: Garantiza que un préstamo no se pierda (transacciones ACID)
- MongoDB: Permite agregar campos al catálogo sin migraciones (ebook, audiobook, etc.)
- Escalabilidad: Catálogo puede crecer a millones de libros sin afectar MySQL
- Performance: Queries de préstamos (frecuentes) son rápidas en MySQL
- Flexibilidad: Cada libro puede tener diferentes metadatos sin cambiar esquema
📊 Ejemplo Real de Consulta Híbrida:
from .models import Prestamo
from django.conf import settings
def prestamos_activos(request):
# 1. Obtener préstamos pendientes de MySQL
prestamos = Prestamo.objects.filter(
fecha_devolucion__isnull=True
).select_related('usuario') # Optimización con JOIN en MySQL
# 2. Conectar a MongoDB
mongo_db = settings.MONGO_CLIENT['biblioteca_catalogo']
# 3. Para cada préstamo, obtener datos del libro
resultado = []
for prestamo in prestamos:
# Buscar libro en MongoDB usando el campo común
libro = mongo_db.libros.find_one({
"libro_id": prestamo.libro_id # ← Campo común
})
# Combinar datos
resultado.append({
'prestamo_id': prestamo.id, # MySQL
'usuario': prestamo.usuario.username, # MySQL (relación FK)
'fecha': prestamo.fecha_prestamo, # MySQL
'titulo': libro['titulo'], # MongoDB
'autor': libro['autor']['nombre'], # MongoDB (subdocumento)
'portada': libro.get('portada_url'), # MongoDB (opcional)
})
return render(request, 'prestamos.html', {'prestamos': resultado})
💡 En esta práctica implementarás:
- ✅ Modelos Django para MySQL (Prestamo, Usuario, Multa)
- ✅ Colecciones MongoDB para catálogo (libros, autores)
- ✅ Campo común
libro_iden ambas BDs - ✅ Views que combinan datos de MySQL y MongoDB
- ✅ Templates que muestran información híbrida
1. 📚 Saber - Dimensión Conceptual
🔵 ¿Qué es un Cluster en MongoDB Atlas?
Un cluster es un conjunto de servidores (nodos) que trabajan juntos para almacenar y gestionar tus datos en MongoDB Atlas. Piensa en él como una "máquina virtual en la nube" dedicada a tu base de datos.
📊 Componentes de un Cluster:
- Nodos (Servers): Computadoras que almacenan copias de tus datos
- Réplicas: Copias redundantes para alta disponibilidad (si un servidor falla, otro toma su lugar)
- Almacenamiento: Espacio en disco donde se guardan tus documentos (512 MB gratis en M0)
- Memoria RAM: Para ejecutar queries rápidamente
- CPU: Procesamiento de operaciones (lecturas/escrituras)
🆓 Cluster Gratuito (M0) en 2026:
| Característica | Versión Gratuita (M0) | Limitación |
|---|---|---|
| Clusters permitidos | ✅ 1 cluster por cuenta | ❌ No puedes crear múltiples clusters |
| Bases de datos | ✅ Ilimitadas dentro del cluster | ✅ Puedes crear: biblioteca_db, logs_db, usuarios_db, etc. |
| Colecciones | ✅ Ilimitadas | ✅ En cada BD: libros, autores, reseñas, etc. |
| Almacenamiento | 512 MB | Suficiente para ~100,000 documentos pequeños |
| RAM | Compartida | Rendimiento moderado para desarrollo |
| Backups automáticos | ❌ No incluidos | Debes hacer backups manuales |
💡 Ejemplo Práctico:
UN CLUSTER = Un "servidor virtual" que contiene:
│
├── biblioteca_logs (Base de Datos 1 - Logs)
│ ├── logs_actividad (Colección)
│ ├── logs_errores (Colección)
│ └── logs_auditoria (Colección)
│
├── biblioteca_catalogo (Base de Datos 2 - Catálogo)
│ ├── libros (Colección)
│ ├── autores (Colección)
│ ├── categorias (Colección)
│ └── reseñas (Colección)
│
├── biblioteca_usuarios (Base de Datos 3 - Usuarios)
│ ├── usuarios (Colección)
│ ├── sesiones (Colección)
│ └── preferencias (Colección)
│
└── biblioteca_estadisticas (Base de Datos 4 - Analytics)
├── visitas (Colección)
├── prestamos (Colección)
└── tendencias (Colección)
✅ PERMITIDO: Crear múltiples bases de datos en el mismo cluster
❌ NO PERMITIDO: Crear un segundo cluster (requiere cuenta de pago)
🔄 ¿Por qué esta limitación?
- Costos de infraestructura: Cada cluster consume recursos reales (CPU, RAM, almacenamiento)
- Prevención de abuso: Evitar que usuarios creen cientos de cuentas gratuitas
- Incentivo a upgrade: Para proyectos grandes, necesitas plan de pago ($0.08/hora M10)
⚠️ IMPORTANTE - Versión Gratuita 2026:
- Solo puedes tener 1 cluster activo por cuenta
- Si intentas crear un segundo cluster, Atlas te pedirá eliminar el primero o pagar
- Solución: Organiza TODAS tus aplicaciones en bases de datos separadas dentro del mismo cluster
- Ejemplo:
biblioteca_db,tienda_db,blog_db→ TODAS en el mismo cluster
¿Qué es NoSQL? (Explicación Completa)
NoSQL (Not Only SQL) son bases de datos no relacionales diseñadas para manejar grandes volúmenes de datos no estructurados, semi-estructurados o con esquemas flexibles. Surgieron en la década de 2000 para resolver limitaciones de las bases de datos SQL tradicionales en contextos de Big Data, alta concurrencia y esquemas variables.
🎯 Diferencias SQL vs NoSQL (Detallado):
| Aspecto | SQL (Relacional) | NoSQL |
|---|---|---|
| Estructura | Tablas con filas y columnas (2D) | Documentos JSON, clave-valor, grafos, columnas |
| Esquema | Fijo y predefinido (CREATE TABLE) | Flexible y dinámico (schema-less o schema-on-read) |
| Escalabilidad | Vertical (más CPU/RAM en 1 servidor) | Horizontal (distribuir en múltiples servidores) |
| Transacciones | ACID completo (Atomicidad, Consistencia, Aislamiento, Durabilidad) | BASE (Basically Available, Soft state, Eventual consistency) |
| Consultas | SQL estándar (SELECT, JOIN, WHERE) | APIs específicas (find, aggregate, get) |
| Relaciones | JOINs entre tablas (Foreign Keys) | Embedding (documentos anidados) o Referencing |
| Velocidad | Lento en escrituras masivas | Rápido en lecturas/escrituras a gran escala |
| Casos de Uso | Sistemas bancarios, ERP, CRM | Redes sociales, IoT, catálogos, logs |
📊 ACID vs BASE - ¿Qué significan?
🔐 ACID (SQL - Bancos, Transacciones Críticas)
- Atomicity (Atomicidad): Transacción completa o nada
- Ejemplo: Transferencia bancaria $100 A→B
- Se ejecuta: Restar $100 de A + Sumar $100 a B
- Si falla cualquier paso, se revierte TODO (rollback)
- Consistency (Consistencia): Datos siempre válidos
- Las reglas de integridad se cumplen SIEMPRE
- Ejemplo: Balance total = $1000 antes y después
- Isolation (Aislamiento): Transacciones no interfieren
- Dos usuarios modificando la misma fila → no se mezclan
- Locks, niveles de aislamiento (READ COMMITTED, etc.)
- Durability (Durabilidad): Datos permanentes tras commit
- Si se confirma la transacción, persiste incluso si el servidor falla
🌐 BASE (NoSQL - Aplicaciones Web, Alta Disponibilidad)
- Basically Available (Básicamente Disponible):
- El sistema responde SIEMPRE, aunque sea con datos parciales
- Prioridad: Disponibilidad > Consistencia inmediata
- Soft state (Estado Suave):
- Los datos pueden cambiar con el tiempo (sin input)
- Ejemplo: Replicación eventual entre servidores
- Eventual consistency (Consistencia Eventual):
- Los datos serán consistentes EVENTUALMENTE (no inmediatamente)
- Ejemplo: Post en red social → visible en 1-2 segundos en todos los nodos
💡 Ejemplo Práctico: Transferencia Bancaria
BEGIN TRANSACTION;
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1; -- Restar de A
UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2; -- Sumar a B
-- Si hay error en cualquier línea:
ROLLBACK; -- ❌ Se cancela TODO
-- Si todo OK:
COMMIT; -- ✅ Cambios permanentes INMEDIATAMENTE
END TRANSACTION;
// Usuario publica post
db.posts.insert_one({
"usuario": "juan123",
"contenido": "¡Hola mundo!",
"fecha": new Date()
});
// ✅ Se guarda INMEDIATAMENTE en el nodo primario
// ⏳ Se replica EVENTUALMENTE a nodos secundarios (1-2 seg)
// Mientras tanto, algunos usuarios pueden NO ver el post
// TRADE-OFF: Disponibilidad rápida > Consistencia inmediata
Tipos de Bases de Datos NoSQL (Explicado con Ejemplos)
| Tipo | Ejemplos | Características | Casos de Uso |
|---|---|---|---|
| Documentales | MongoDB, CouchDB, Firestore | Almacena documentos JSON/BSON con estructura flexible | Apps web, catálogos de productos, CMS |
| Clave-Valor | Redis, DynamoDB, Memcached | Par clave-valor simple (como un diccionario) | Caché, sesiones de usuario, contadores |
| Columnares | Cassandra, HBase, ScyllaDB | Almacena datos por columnas (no por filas) | Big Data, analytics, series temporales |
| Grafos | Neo4j, ArangoDB, OrientDB | Nodos conectados por relaciones (aristas) | Redes sociales, recomendaciones, rutas |
📝 Ejemplos de Cada Tipo de NoSQL
1️⃣ Documental (MongoDB)
{
"_id": ObjectId("..."),
"nombre": "Juan Pérez",
"email": "juan@example.com",
"direcciones": [ // Array de subdocumentos
{ "tipo": "casa", "calle": "Av. 123", "ciudad": "CDMX" },
{ "tipo": "trabajo", "calle": "Calle 456", "ciudad": "GDL" }
],
"preferencias": { // Documento anidado
"tema": "oscuro",
"idioma": "es"
}
}
2️⃣ Clave-Valor (Redis)
// Guardar sesión
SET session:abc123 "{'user_id': 42, 'name': 'Juan', 'role': 'admin'}"
EXPIRE session:abc123 3600 // Expira en 1 hora
// Obtener sesión
GET session:abc123 // → "{'user_id': 42, ...}"
// Contador de visitas
INCR page_views:home // Incrementa atómicamente
3️⃣ Columnar (Cassandra)
// CREATE TABLE events (
// user_id UUID,
// timestamp TIMESTAMP,
// event_type TEXT,
// data TEXT,
// PRIMARY KEY (user_id, timestamp)
// );
// Los datos se almacenan por columnas para queries analíticas:
// Fila: user_id | timestamp | event_type | data
// Pero físicamente:
// Columna user_id: [uuid1, uuid2, uuid3, ...]
// Columna timestamp: [t1, t2, t3, ...]
// Columna event_type: ["click", "view", ...]
4️⃣ Grafos (Neo4j)
// Crear nodos y relaciones
CREATE (juan:Usuario {nombre: "Juan"})
CREATE (maria:Usuario {nombre: "María"})
CREATE (juan)-[:SIGUE]->(maria)
CREATE (juan)-[:AMIGO_DE]->(pedro)
// Query: ¿Quiénes son amigos de amigos de Juan?
MATCH (juan:Usuario {nombre: "Juan"})-[:AMIGO_DE*2]-(amigo)
RETURN amigo.nombre
🔍 ¿Cuándo Usar Cada Tipo?
- Documentales (MongoDB): Cuando tus objetos de negocio tienen estructura compleja
- ✓ E-commerce (productos con atributos variables)
- ✓ CMS (artículos, páginas con metadatos)
- ✓ Catálogos con jerarquías
- Clave-Valor (Redis): Cuando necesitas acceso ultra-rápido a valores simples
- ✓ Cache de consultas
- ✓ Sesiones de usuario
- ✓ Rate limiting (límite de peticiones)
- Columnares (Cassandra): Cuando procesas enormes volúmenes de datos
- ✓ Logs de aplicaciones (millones de eventos/día)
- ✓ Analytics de Big Data
- ✓ IoT (sensores enviando datos constantemente)
- Grafos (Neo4j): Cuando las relaciones son tan importantes como los datos
- ✓ Redes sociales (¿quién conoce a quién?)
- ✓ Detección de fraude (patrones sospechosos)
- ✓ Motores de recomendación
MongoDB - Base de Datos Documental (Explicación)
MongoDB es la base de datos NoSQL más popular del mundo (usado por Uber, Airbnb, eBay, LinkedIn). Almacena datos en documentos similares a JSON con esquemas dinámicos, lo que facilita la integración con aplicaciones modernas. MongoDB usa BSON (Binary JSON) internamente para mayor eficiencia.
🌟 ¿Por Qué MongoDB es tan Popular?
- Esquema Flexible: Agregar campos sin migraciones
- Documento 1:
{"nombre": "Juan", "edad": 30} - Documento 2:
{"nombre": "María", "edad": 25, "ciudad": "CDMX"}← ¡Nuevo campo! - ✅ Sin error, sin ALTER TABLE
- Documento 1:
- Alta Escalabilidad: Sharding horizontal automático
- Divide datos entre múltiples servidores
- Ejemplo: 1TB de datos → 10 servidores con 100GB cada uno
- Replicación Nativa: Alta disponibilidad
- Replica Set: 1 primario + 2+ secundarios
- Si el primario falla → secundario se promociona automáticamente
- Queries Potentes: Agregaciones, índices geoespaciales
- Buscar documentos por ubicación GPS
- Pipelines de agregación (como GROUP BY en SQL)
- Integración con JavaScript: JSON nativo
- Frontend envía JSON → MongoDB almacena BSON → Backend devuelve JSON
- Sin conversión de datos
| Concepto SQL | Equivalente MongoDB | Descripción Detallada |
|---|---|---|
| Base de Datos | Database | Contenedor lógico de colecciones (ej: biblioteca_db) |
| Tabla | Collection | Grupo de documentos (ej: libros, usuarios) |
| Fila/Registro | Document | Objeto JSON/BSON individual (ej: un libro específico) |
| Columna | Field | Clave en un documento (ej: "titulo", "precio") |
| JOIN | Embedding/Referencing/$lookup | Documentos anidados o referencias entre colecciones |
| PRIMARY KEY | _id | Identificador único (ObjectId de 12 bytes, generado automáticamente) |
| INDEX | Index | Índices B-tree, hash, geoespaciales, text (búsqueda full-text) |
| SELECT * FROM | db.collection.find() | Retorna cursor con documentos que cumplen criterio |
| INSERT INTO | db.collection.insert_one() | Inserta un documento, genera _id si no existe |
| UPDATE | db.collection.update_one() | Actualiza campos específicos con operadores ($set, $inc) |
| DELETE FROM | db.collection.delete_one() | Elimina documento que cumple filtro |
📊 Comparación SQL vs MongoDB (Mismo Caso de Uso)
Escenario: Almacenar Libro con Autor
-- Tabla autores
CREATE TABLE autores (
id INT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(200),
pais VARCHAR(100),
fecha_nacimiento DATE
);
-- Tabla libros
CREATE TABLE libros (
id INT PRIMARY KEY AUTO_INCREMENT,
titulo VARCHAR(200),
isbn VARCHAR(20),
autor_id INT, -- ⚠️ Foreign Key
precio DECIMAL(10,2),
stock INT,
FOREIGN KEY (autor_id) REFERENCES autores(id)
);
-- Insertar datos (2 queries separados)
INSERT INTO autores (nombre, pais, fecha_nacimiento)
VALUES ('Gabriel García Márquez', 'Colombia', '1927-03-06');
-- Supongamos que retorna id = 1
INSERT INTO libros (titulo, isbn, autor_id, precio, stock)
VALUES ('Cien Años de Soledad', '978-0307474728', 1, 15.99, 45);
-- Consultar (requiere JOIN)
SELECT l.titulo, l.isbn, l.precio, a.nombre AS autor, a.pais
FROM libros l
INNER JOIN autores a ON l.autor_id = a.id
WHERE l.titulo LIKE '%Cien%';
// Insertar todo en UN SOLO documento (1 query)
db.libros.insert_one({
"titulo": "Cien Años de Soledad",
"isbn": "978-0307474728",
"autor": { // ⭐ Documento anidado (embedding)
"nombre": "Gabriel García Márquez",
"pais": "Colombia",
"fecha_nacimiento": ISODate("1927-03-06")
},
"precio": 15.99,
"stock": 45
})
// Consultar (SIN JOIN, más rápido)
db.libros.find({ "titulo": { $regex: "Cien" } })
✅ Ventajas MongoDB en este caso:
- Una sola operación de inserción
- No necesita JOIN (consulta más rápida)
- Datos relacionados juntos (localidad de datos)
- Fácil de serializar a JSON para API
⚠️ Desventajas MongoDB en este caso:
- Si el autor escribe 100 libros → datos de autor duplicados 100 veces
- Si cambia el país del autor → hay que actualizar 100 documentos
- Solución: Usar
Referencing(similar a FK) cuando hay duplicación
🔗 Embedding vs Referencing en MongoDB
| Embedding (Anidado) | Referencing (Referencias) |
|---|---|
| Cuándo: Relación 1-a-pocos, datos leídos juntos | Cuándo: Relación muchos-a-muchos, datos grandes |
| Ventaja: 1 sola query, ultra rápido | Ventaja: Sin duplicación, actualizaciones centralizadas |
| Desventaja: Duplicación de datos | Desventaja: Requiere múltiples queries o $lookup |
| Ejemplo: Libro + Autor (si autor escribe 1-2 libros) | Ejemplo: Libro + Categorías (misma categoría en 1000+ libros) |
// Colección autores (separada)
{
"_id": ObjectId("60a1b2c3d4e5f6g7h8i9j0k1"),
"nombre": "Gabriel García Márquez",
"pais": "Colombia"
}
// Colección libros (referencia al autor)
{
"_id": ObjectId("..."),
"titulo": "Cien Años de Soledad",
"autor_id": ObjectId("60a1b2c3d4e5f6g7h8i9j0k1"), // ← Referencia
"precio": 15.99
}
// Query con $lookup (JOIN de MongoDB)
db.libros.aggregate([
{
$lookup: {
from: "autores",
localField: "autor_id",
foreignField: "_id",
as: "autor"
}
}
])
2. 🌐 MongoDB y MongoDB Atlas
¿Qué es MongoDB Atlas?
MongoDB Atlas es la plataforma de base de datos en la nube totalmente administrada de MongoDB. Ofrece despliegue automatizado, monitoreo, respaldos automáticos y escalabilidad.
🌟 Ventajas de MongoDB Atlas:
- Tier Gratuito: 512 MB de almacenamiento gratis
- Sin Configuración: Cluster listo en minutos
- Alta Disponibilidad: Replicación automática
- Backups Automáticos: Puntos de restauración
- Seguridad: Encriptación y autenticación
- Escalabilidad: Crece según tus necesidades
Estructura de Datos en MongoDB
{
"_id": ObjectId("507f1f77bcf86cd799439011"), // ID único generado automáticamente
"titulo": "Cien Años de Soledad", // Campo de texto
"isbn": "978-0307474728", // ISBN del libro
"autor": { // Documento anidado (embedding)
"nombre": "Gabriel García Márquez",
"pais": "Colombia",
"fecha_nacimiento": ISODate("1927-03-06") // Tipo Date de MongoDB
},
"categorias": ["Realismo Mágico", "Literatura"], // Array de strings
"precio": 15.99, // Número decimal
"stock": 45, // Número entero
"disponible": true, // Booleano
"resenas": [ // Array de documentos anidados
{
"usuario": "usuario123",
"calificacion": 5,
"comentario": "Obra maestra",
"fecha": ISODate("2026-01-10")
}
],
"metadata": { // Metadata adicional flexible
"fecha_creacion": ISODate("2026-01-01"),
"ultima_actualizacion": ISODate("2026-01-15")
}
}
3. ⚙️ Configuración de MongoDB Atlas (PASO A PASO DETALLADO)
⏱️ FASE 1: Crear Cuenta MongoDB Atlas (20 minutos)
Objetivo: Registrar cuenta gratuita y configurar organización inicial
Requisitos: Email válido, conexión a internet
📋 Paso 1: Registro de Cuenta (5 min)
- Abre tu navegador (Chrome, Firefox, Edge)
- Ve a: https://www.mongodb.com/cloud/atlas/register
- Espera a que cargue el formulario de registro
💡 IMPORTANTE: Registro con Google vs Manual
Si usas "Sign up with Google":
- ✅ MongoDB tomará automáticamente tu nombre y apellido de tu cuenta de Google
- ✅ NO te pedirá llenar el formulario de registro completo
- ✅ Irás directo a la pantalla de bienvenida (más rápido)
- ✅ NO necesitarás verificar email (Google ya lo verificó)
Si usas "Registro Manual":
- 📝 Deberás llenar todos los campos del formulario
- 📧 Recibirás un email de verificación
- ⏳ Tomará 2-3 minutos más
Opción 1: Registro con Google (⚡ MÁS RÁPIDO - RECOMENDADO):
- 🔘 Haz clic en el botón "Sign up with Google"
- 📧 Selecciona tu cuenta de Gmail
- ✅ Autoriza el acceso cuando aparezca el popup
- 🎉 Listo! Irás directo al dashboard (NO hay formulario adicional)
Opción 2: Registro manual (solo si no tienes Gmail):
- ✍️ First Name: Tu nombre
- ✍️ Last Name: Tu apellido
- 📧 Email: tu_email@outlook.com (o cualquier email)
- 🔑 Password: Contraseña segura (mín. 8 caracteres, con mayúsculas y números)
- ☑️ Marca la casilla "I agree to the Terms of Service and Privacy Policy"
- 🟢 Haz clic en "Create Account"
- 📬 Abre tu bandeja de entrada de email
- 🔍 Busca email de "MongoDB Cloud" o "noreply@mongodb.com"
- 📧 Abre el email (revisa spam si no aparece)
- 🔗 Haz clic en el botón "Verify Email"
- ✅ Deberías ver mensaje: "Email verified successfully"
✅ CHECKPOINT 1: Cuenta creada exitosamente
Deberías ver:
- ✅ Pantalla de bienvenida de MongoDB Atlas
- ✅ Mensaje "Welcome to MongoDB Atlas"
- ✅ Tu email en la esquina superior derecha
Si NO ves esto: Revisa tu email y verifica tu cuenta antes de continuar
📋 Paso 2: Configurar Organización y Proyecto (5 min)
Atlas te hará algunas preguntas. Responde así:
- What is your goal today? → Selecciona "Learn MongoDB"
- What type of application are you building? → Selecciona "Web Application"
- What is your preferred language? → Selecciona "Python"
- 🟢 Haz clic en "Finish"
- 🏢 En la pantalla principal, localiza la sección "Organizations"
- Si ya tienes una organización creada automáticamente, puedes usarla
- Si quieres crear una nueva: Haz clic en "Create New Organization"
- 📝 Organization Name:
UTH-2026 - 🟢 Haz clic en "Next"
- ⏭️ En "Add Members" → Haz clic en "Create Organization" (puedes saltarte este paso)
- 📁 Haz clic en "New Project" (botón verde en la esquina superior derecha)
- 📝 Project Name:
Biblioteca-API - 🟢 Haz clic en "Next"
- ⏭️ En "Add Members" → Haz clic en "Create Project" (puedes saltarte este paso)
✅ CHECKPOINT 2: Proyecto creado
Deberías ver:
- ✅ Mensaje "Project Created Successfully"
- ✅ Nombre del proyecto "Biblioteca-API" en la parte superior
- ✅ Botón verde "Build a Database" o "Create a Deployment"
📋 Paso 3: Crear Cluster Gratuito (10 min)
⏱️ FASE 2: Configurar Cluster de MongoDB (15 minutos)
Objetivo: Crear base de datos gratuita en la nube (512 MB)
⚠️ IMPORTANTE - LIMITACIÓN VERSIÓN GRATUITA 2026:
- 🔴 Solo puedes crear 1 cluster gratuito (M0) por cuenta
- ❌ Si intentas crear un segundo cluster, Atlas te pedirá:
- Eliminar el cluster existente, O
- Actualizar a un plan de pago (M10 desde $0.08/hora)
- ✅ Solución: Crea MÚLTIPLES BASES DE DATOS dentro del mismo cluster
- Ejemplo:
biblioteca_logs,biblioteca_catalogo,biblioteca_usuarios,biblioteca_estadisticas - Cada base de datos puede tener múltiples colecciones
- Todas comparten los 512 MB del cluster gratuito
- Ejemplo:
🏗️ Arquitectura Recomendada 2026:
BibliotecaCluster (Tu único cluster M0 gratuito)
│
├── biblioteca_logs (Base de Datos 1 - Logs)
│ ├── logs_actividad
│ ├── logs_errores
│ └── logs_auditoria
│
├── biblioteca_catalogo (Base de Datos 2 - Catálogo)
│ ├── libros
│ ├── autores
│ ├── categorias
│ └── reseñas
│
├── biblioteca_usuarios (Base de Datos 3 - Usuarios)
│ ├── usuarios
│ ├── sesiones
│ └── preferencias
│
└── biblioteca_estadisticas (Base de Datos 4 - Analytics)
├── visitas
├── prestamos
└── tendencias
✅ Esta estructura te permite tener TODO en el mismo cluster gratuito
✅ Sin necesidad de crear múltiples clusters
- 🟢 Haz clic en el botón grande verde "Build a Database" o "Create"
- 💚 Espera a que cargue la página de opciones de deployment
- ⚠️ RECUERDA: Este será tu ÚNICO cluster gratuito, nómbralo bien
Verás 3 opciones de planes. Elige esta:
✅ SELECCIONA: M0 FREE (Shared)
- 💰 Price: FREE (forever)
- 💾 Storage: 512 MB
- 🌐 Shared RAM: Shared vCPU
- 📊 Ideal para: Aprendizaje, desarrollo, proyectos pequeños
🟢 Haz clic en el botón "Create" debajo de esta opción
❌ NO SELECCIONES:
- ❌ M10: Dedicated ($0.08/hr) - Este es PAGO
- ❌ Serverless: Pay as you go - Este también es PAGO
Ahora configura estos campos EXACTAMENTE como se indica:
| Campo | Valor Recomendado | ¿Dónde está? |
|---|---|---|
| Cloud Provider | ✅ AWS (Amazon Web Services) | Primera sección, tres opciones: AWS, Google Cloud, Azure |
| Region | ✅ us-east-1 (N. Virginia) o cualquier región con ⭐ FREE TIER AVAILABLE |
Lista desplegable con regiones del mundo |
| Cluster Tier | ✅ M0 Sandbox (ya seleccionado) | No lo cambies, debe decir "FREE" o "$0" |
| Cluster Name | ✅ BibliotecaCluster(sin espacios, sin acentos) |
Campo de texto en la parte inferior |
🟢 Haz clic en "Create Deployment" o "Create"
⏳ Espera mientras se crea el cluster...
Verás una pantalla de carga con mensaje: "Creating your cluster..."
Tiempo de espera: 1-3 minutos
NO cierres la ventana ni refresques la página
📋 Paso 4: Configurar Seguridad (Usuario y Red)
🔒 IMPORTANTE: Configuración de Seguridad
Atlas te pedirá configurar DOS cosas críticas: Usuario de BD y Acceso de Red
Deberías ver un formulario titulado "Security Quickstart". Completa así:
# GUARDA ESTOS DATOS EN UN LUGAR SEGURO (Notepad, bloc de notas)
Username: biblioteca_admin
Password: Biblioteca2026! # Usa esta o crea una más segura
# IMPORTANTE: Copia EXACTAMENTE como los escribiste
# La contraseña es CASE-SENSITIVE (distingue mayúsculas)
- 📝 Authentication Method: Deja seleccionado "Password"
- 👤 Username: Escribe
biblioteca_admin - 🔑 Password: Escribe
Biblioteca2026!(o tu contraseña preferida) - 💾 COPIA Y GUARDA usuario y contraseña en un archivo .txt en tu computadora
- ☑️ Database User Privileges: Deja "Atlas admin" o "Read and write to any database"
- 🟢 Haz clic en "Create User"
Ahora verás una sección "Where would you like to connect from?"
Opción 1: Acceso desde cualquier IP (DESARROLLO - MÁS FÁCIL)
- 🌐 Haz clic en "Add My Current IP Address"
- Verás tu IP agregada (algo como: 192.168.x.x)
- 📝 Para permitir CUALQUIER IP (útil para desarrollo):
- Haz clic en "Add a Different IP Address"
- En el campo "IP Address" escribe:
0.0.0.0/0 - En "Description" escribe:
Acceso desarrollo - Haz clic en "Add Entry"
- 🟢 Haz clic en "Finish and Close"
⚠️ Seguridad en Producción:
- ❌
0.0.0.0/0permite acceso desde CUALQUIER IP del mundo - ✅ Está bien para DESARROLLO y APRENDIZAJE
- ❌ NUNCA uses esto en producción con datos reales
- ✅ En producción: Agrega solo IPs específicas de tu servidor
✅ CHECKPOINT 3: Cluster configurado exitosamente
Deberías ver:
- ✅ Mensaje "Congratulations on setting up access rules!"
- ✅ Tu cluster "BibliotecaCluster" aparece en el dashboard
- ✅ Estado del cluster: "Active" (bolita verde)
- ✅ Botón "Connect" disponible
Si el cluster dice "Creating..." espera 1-2 minutos más
📋 Paso 5: Obtener Connection String (MUY IMPORTANTE)
🔗 ¿Qué es el Connection String?
Es la "dirección de tu base de datos" que Django usará para conectarse a MongoDB Atlas.
Formato: mongodb+srv://usuario:contraseña@servidor/basededatos
- 🔍 Localiza tu cluster "BibliotecaCluster" en el dashboard
- 🔘 Haz clic en el botón "Connect" (al lado derecho del nombre del cluster)
- 💚 Aparecerá un modal con varias opciones de conexión
🎯 ¿Qué Opción Seleccionar?
Aparecerá un modal con 5 opciones de conexión. Para este proyecto Django, selecciona:
✅ Drivers → "Access your Atlas data using MongoDB's native drivers"
| Opción | ¿Para Qué Sirve? | ¿Usarlo en Este Proyecto? |
|---|---|---|
|
✅ Drivers "Access your Atlas data using MongoDB's native drivers (e.g. Node.js, Go, etc.)" |
Obtener connection string para conectar desde código (Python, Node.js, Java, etc.) |
✅ SÍ - Selecciona esta Para Django con PyMongo |
|
❌ Compass "Explore, modify, and visualize your data with MongoDB's GUI" |
Aplicación de escritorio para explorar datos visualmente (como MySQL Workbench) |
❌ NO - No la necesitas (Opcional para ver datos visualmente) |
|
❌ Shell "Quickly add & update data using MongoDB's Javascript command-line interface" |
Línea de comandos para ejecutar queries directamente (como mysql CLI) |
❌ NO - No la necesitas (Útil para debugging avanzado) |
|
❌ MongoDB for VS Code "Work with your data in MongoDB directly from your VS Code environment" |
Extensión de VS Code para ver y editar datos desde el editor |
❌ NO - No la necesitas (Opcional para ver datos en VS Code) |
|
❌ Atlas SQL "Easily connect SQL tools to Atlas for data analysis and visualization" |
Conectar herramientas SQL tradicionales a MongoDB (BI tools) |
❌ NO - No la necesitas (Para reportes con Tableau, Power BI) |
⚠️ IMPORTANTE - Pasos Exactos:
- Haz clic en el botón "Connect" de tu cluster
- En el modal, busca y haz clic en "Drivers" (primera opción generalmente)
- Verás el texto: "Access your Atlas data using MongoDB's native drivers"
- NO selecciones Compass, Shell, VS Code ni Atlas SQL para este proyecto
- 📦 Driver: Selecciona "Python"
- 🔢 Version: Selecciona "4.0 or later"
Verás un recuadro con código similar a este:
mongodb+srv://biblioteca_admin:<password>@bibliotecacluster.xxxxx.mongodb.net/?retryWrites=true&w=majority
🔄 Debes MODIFICARLO así:
- 📋 Copia el connection string completo (botón "Copy" al lado)
- 📝 Pégalo en Notepad o cualquier editor de texto
- 🔑 Reemplaza
<password>con tu contraseña REAL:Biblioteca2026! - 🗄️ Agrega el nombre de la base de datos después de
.mongodb.net/
# ANTES (incorrecto):
mongodb+srv://biblioteca_admin:<password>@bibliotecacluster.xxxxx.mongodb.net/?retryWrites=true&w=majority
# DESPUÉS (correcto):
mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/biblioteca_logs?retryWrites=true&w=majority
# Componentes explicados:
# mongodb+srv:// → Protocolo de conexión (SRV para Atlas)
# biblioteca_admin: → Tu usuario de BD
# Biblioteca2026! → Tu contraseña (SIN los símbolos < >)
# @bibliotecacluster → Nombre de tu cluster
# .xxxxx.mongodb.net → Servidor de Atlas (único para tu cluster)
# /biblioteca_logs → Nombre de la base de datos (la que crearemos)
# ?retryWrites=true → Parámetros de configuración
- 💾 Crea un archivo
mongodb_credentials.txten tu proyecto - 📝 Pega el connection string COMPLETO y MODIFICADO
- 🔒 IMPORTANTE: Agrega este archivo a
.gitignoresi usas Git - ❌ NUNCA subas este archivo a GitHub o lo compartas públicamente
✅ CHECKPOINT 4: Connection String listo
Verifica que tu connection string tenga:
- ✅ Tu usuario:
biblioteca_admin - ✅ Tu contraseña SIN los símbolos
< > - ✅ El nombre del cluster:
bibliotecacluster.xxxxx - ✅ El nombre de la BD:
/biblioteca_logs - ✅ Guardado en un archivo .txt seguro
# Puedes probar la conexión con este comando Python:
python -c "from pymongo import MongoClient; client = MongoClient('TU_CONNECTION_STRING'); print('✅ Conexión exitosa:', client.server_info()['version'])"
# Si ves "✅ Conexión exitosa: 7.x.x" → Todo bien!
# Si ves error → Revisa usuario, contraseña o IP whitelist
⚠️ Seguridad del Connection String:
- 🔒 Contiene credenciales sensibles (usuario y contraseña en texto plano)
- ❌ NUNCA lo incluyas directamente en tu código fuente
- ✅ USA variables de entorno en producción
- ✅ Agrega a .gitignore:
*.txtomongodb_credentials.txt - 🔄 Rota contraseñas cada 3-6 meses en producción
📋 Paso 6: Crear Múltiples Bases de Datos en el Cluster (NUEVA SECCIÓN 2026)
🏗️ Cómo Organizar Bases de Datos en Tu Único Cluster Gratuito
Como solo puedes tener 1 cluster M0, es fundamental organizar correctamente tus bases de datos dentro de él.
📊 Estrategias de Organización 2026:
| Estrategia | Cuándo Usarla | Ejemplo |
|---|---|---|
| Por Proyecto | Tienes múltiples proyectos Django | biblioteca_db, tienda_db, blog_db |
| Por Funcionalidad | Un solo proyecto grande | biblioteca_catalogo, biblioteca_logs, biblioteca_analytics |
| Por Ambiente | Desarrollo vs Testing | biblioteca_dev, biblioteca_test, biblioteca_prod |
MongoDB Atlas crea bases de datos automáticamente cuando insertas el primer documento. No necesitas crearlas manualmente en Atlas.
from pymongo import MongoClient
# Connection string SIN nombre de BD específico:
MONGO_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/?retryWrites=true&w=majority"
client = MongoClient(MONGO_URI)
# ✅ Método 1: Acceder a múltiples bases de datos
db_logs = client['biblioteca_logs'] # Para logs del sistema
db_catalogo = client['biblioteca_catalogo'] # Para catálogo de libros
db_usuarios = client['biblioteca_usuarios'] # Para datos de usuarios
db_estadisticas = client['biblioteca_estadisticas'] # Para analytics y reportes
# ✅ Método 2: Usar notación punto (alternativa)
db_logs = client.biblioteca_logs
db_catalogo = client.biblioteca_catalogo
db_usuarios = client.biblioteca_usuarios
db_estadisticas = client.biblioteca_estadisticas
# 🗄️ Insertar dato en cada BD (esto las crea automáticamente):
db_logs['logs_actividad'].insert_one({
"evento": "inicio",
"fecha": "2026-01-30",
"usuario": "admin"
})
db_catalogo['libros'].insert_one({
"libro_id": 1,
"titulo": "Cien Años de Soledad",
"isbn": "978-0307474728",
"autor": {"nombre": "Gabriel García Márquez"}
})
db_usuarios['usuarios'].insert_one({
"nombre": "Admin",
"rol": "admin",
"email": "admin@biblioteca.com"
})
db_estadisticas['visitas'].insert_one({
"fecha": "2026-01-30",
"total_visitas": 120,
"libros_prestados": 15
})
# 📋 Ver todas las bases de datos creadas:
print("Bases de datos en el cluster:", client.list_database_names())
# Output: ['admin', 'biblioteca_logs', 'biblioteca_catalogo', 'biblioteca_usuarios', 'biblioteca_estadisticas', 'local']
- 🌐 Ve a cloud.mongodb.com
- 🔍 Haz clic en tu cluster "BibliotecaCluster"
- 📊 Haz clic en "Browse Collections"
- 👁️ Verás TODAS las bases de datos de tu cluster:
biblioteca_logs→ logs_actividad (colección)biblioteca_catalogo→ libros (colección)biblioteca_usuarios→ usuarios (colección)biblioteca_estadisticas→ visitas (colección)
- ✅ Cada base de datos aparece como un nodo expandible
- 💡 Al expandir cada BD verás sus colecciones internas
Para proyectos grandes, configura múltiples conexiones en Django:
# Configuración para usar múltiples BD en el mismo cluster
from pymongo import MongoClient
# Connection string BASE (sin nombre de BD específico):
MONGODB_BASE_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/?retryWrites=true&w=majority"
# Cliente MongoDB compartido:
mongo_client = MongoClient(MONGODB_BASE_URI)
# Acceso a diferentes bases de datos:
MONGODB_DATABASES = {
'logs': mongo_client['biblioteca_logs'],
'catalogo': mongo_client['biblioteca_catalogo'],
'usuarios': mongo_client['biblioteca_usuarios'],
'estadisticas': mongo_client['biblioteca_estadisticas'],
}
# Uso en views.py:
# from django.conf import settings
# db_logs = settings.MONGODB_DATABASES['logs']
# db_catalogo = settings.MONGODB_DATABASES['catalogo']
# db_estadisticas = settings.MONGODB_DATABASES['estadisticas']
💡 Buenas Prácticas para Organizar BDs en el Mismo Cluster:
- Nomenclatura consistente: Usa prefijos comunes (
biblioteca_*) - Separación lógica: Una BD por módulo funcional
- Documentación: Mantén un README indicando qué guarda cada BD
- Monitoreo: Revisa el uso de almacenamiento en Atlas (max 512 MB total)
- Limpieza: Elimina BDs de prueba que ya no uses
from pymongo import MongoClient
client = MongoClient(MONGO_URI)
for db_name in client.list_database_names():
if 'biblioteca' in db_name: # Solo nuestras BDs
db = client[db_name]
stats = db.command("dbStats")
size_mb = stats['dataSize'] / (1024 * 1024)
print(f"📊 {db_name}: {size_mb:.2f} MB")
# Output ejemplo:
# 📊 biblioteca_logs: 0.05 MB
# 📊 biblioteca_catalogo: 2.34 MB
# 📊 biblioteca_usuarios: 0.12 MB
# 📊 biblioteca_estadisticas: 0.08 MB
# 📊 Total usado: ~2.59 MB / 512 MB (0.5% del límite gratuito)
✅ CHECKPOINT 5: Bases de datos organizadas
Deberías poder:
- ✅ Conectar a tu cluster desde Django
- ✅ Crear múltiples bases de datos programáticamente
- ✅ Acceder a diferentes BDs usando
client['nombre_bd'] - ✅ Ver todas tus BDs en Atlas dashboard → Browse Collections
- ✅ Monitorear uso de almacenamiento (debe estar bajo 512 MB)
📋 Paso 7: Crear la Estructura Completa de Bases de Datos y Colecciones
🎯 Objetivo: Crear las 4 Bases de Datos con sus Colecciones y Campos
Ahora vamos a crear la estructura completa del proyecto siguiendo la arquitectura definida:
Crea un archivo Python que generará toda la estructura de bases de datos:
"""
Script para inicializar la estructura completa de MongoDB Atlas
Crea las 4 bases de datos con sus colecciones y documentos de ejemplo
"""
from pymongo import MongoClient
from datetime import datetime
from bson.objectid import ObjectId
# ========================================
# 1. CONEXIÓN A MONGODB ATLAS
# ========================================
# Reemplaza con tu connection string real
MONGO_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/?retryWrites=true&w=majority"
print("🔌 Conectando a MongoDB Atlas...")
client = MongoClient(MONGO_URI)
# Probar conexión
try:
client.admin.command('ping')
print("✅ Conexión exitosa a MongoDB Atlas")
except Exception as e:
print(f"❌ Error de conexión: {e}")
exit(1)
# ========================================
# 2. BASE DE DATOS 1: biblioteca_logs
# ========================================
print("\n📁 Creando Base de Datos: biblioteca_logs")
db_logs = client['biblioteca_logs']
# Colección 1: logs_actividad
logs_actividad = db_logs['logs_actividad']
logs_actividad.insert_many([
{
"timestamp": datetime.now(),
"usuario": "admin",
"accion": "LOGIN",
"ip": "192.168.1.100",
"detalles": "Inicio de sesión exitoso"
},
{
"timestamp": datetime.now(),
"usuario": "admin",
"accion": "PRESTAMO_CREADO",
"libro_id": 1, # ← Campo común con MySQL
"detalles": "Préstamo registrado"
}
])
print(" ✅ logs_actividad creada (2 documentos)")
# Colección 2: logs_errores
logs_errores = db_logs['logs_errores']
logs_errores.insert_one({
"timestamp": datetime.now(),
"nivel": "ERROR",
"modulo": "prestamos",
"mensaje": "Libro no disponible en stock",
"libro_id": 5,
"stack_trace": "..."
})
print(" ✅ logs_errores creada")
# Colección 3: logs_auditoria
logs_auditoria = db_logs['logs_auditoria']
logs_auditoria.insert_one({
"timestamp": datetime.now(),
"usuario": "admin",
"tabla": "prestamos",
"accion": "UPDATE",
"registro_id": 10,
"cambios": {
"campo": "estado",
"valor_anterior": "activo",
"valor_nuevo": "devuelto"
}
})
print(" ✅ logs_auditoria creada")
# ========================================
# 3. BASE DE DATOS 2: biblioteca_catalogo
# ========================================
print("\n📁 Creando Base de Datos: biblioteca_catalogo")
db_catalogo = client['biblioteca_catalogo']
# Colección 1: libros (⚠️ IMPORTANTE: Campo libro_id para conectar con MySQL)
libros = db_catalogo['libros']
libros_data = [
{
"libro_id": 1, # ← CAMPO COMÚN con MySQL (INT)
"titulo": "Cien Años de Soledad",
"isbn": "978-0307474728",
"autor": { # Subdocumento
"nombre": "Gabriel García Márquez",
"pais": "Colombia",
"fecha_nacimiento": datetime(1927, 3, 6)
},
"categorias": ["Realismo Mágico", "Literatura Latinoamericana"],
"editorial": "Penguin Random House",
"año_publicacion": 1967,
"stock": 10,
"precio": 15.99,
"disponible": True,
"portada_url": "https://example.com/cien-anos.jpg",
"descripcion": "Historia de la familia Buendía...",
"paginas": 417,
"idioma": "Español"
},
{
"libro_id": 2, # ← CAMPO COMÚN
"titulo": "Don Quijote de la Mancha",
"isbn": "978-0060934347",
"autor": {
"nombre": "Miguel de Cervantes",
"pais": "España",
"fecha_nacimiento": datetime(1547, 9, 29)
},
"categorias": ["Clásicos", "Novela"],
"editorial": "Editorial Planeta",
"año_publicacion": 1605,
"stock": 5,
"precio": 18.99,
"disponible": True,
"formato_ebook": True, # Campo flexible (no todos lo tienen)
"paginas": 863,
"idioma": "Español"
},
{
"libro_id": 3,
"titulo": "1984",
"isbn": "978-0451524935",
"autor": {
"nombre": "George Orwell",
"pais": "Reino Unido"
},
"categorias": ["Distopía", "Ciencia Ficción"],
"stock": 8,
"precio": 12.99,
"disponible": True,
"audiobook_disponible": True # Otro campo flexible
}
]
libros.insert_many(libros_data)
print(f" ✅ libros creada ({len(libros_data)} documentos)")
# Colección 2: autores
autores = db_catalogo['autores']
autores.insert_many([
{
"nombre_completo": "Gabriel García Márquez",
"pais": "Colombia",
"biografia": "Premio Nobel de Literatura 1982...",
"libros_escritos": [1] # Referencias a libro_id
},
{
"nombre_completo": "Miguel de Cervantes",
"pais": "España",
"biografia": "Autor del Quijote...",
"libros_escritos": [2]
}
])
print(" ✅ autores creada")
# Colección 3: categorias
categorias = db_catalogo['categorias']
categorias.insert_many([
{"nombre": "Realismo Mágico", "descripcion": "Narrativa con elementos mágicos"},
{"nombre": "Clásicos", "descripcion": "Obras literarias atemporales"},
{"nombre": "Distopía", "descripcion": "Sociedades futuras opresivas"}
])
print(" ✅ categorias creada")
# Colección 4: reseñas
reseñas = db_catalogo['reseñas']
reseñas.insert_many([
{
"libro_id": 1, # ← CAMPO COMÚN
"usuario": "juan123",
"calificacion": 5,
"comentario": "Una obra maestra de la literatura",
"fecha": datetime.now(),
"likes": 15
},
{
"libro_id": 2,
"usuario": "maria456",
"calificacion": 4,
"comentario": "Clásico imprescindible",
"fecha": datetime.now()
}
])
print(" ✅ reseñas creada")
# ========================================
# 4. BASE DE DATOS 3: biblioteca_usuarios
# ========================================
print("\n📁 Creando Base de Datos: biblioteca_usuarios")
db_usuarios = client['biblioteca_usuarios']
# Colección 1: usuarios
usuarios = db_usuarios['usuarios']
usuarios.insert_many([
{
"usuario_id": 1, # Referencia al auth_user de MySQL
"username": "admin",
"perfil": {
"foto_url": "https://example.com/admin.jpg",
"biografia": "Administrador del sistema",
"generos_favoritos": ["Realismo Mágico", "Ciencia Ficción"]
},
"historial_lectura": [
{"libro_id": 1, "fecha_inicio": datetime(2026, 1, 15), "terminado": True},
{"libro_id": 3, "fecha_inicio": datetime(2026, 1, 20), "terminado": False}
]
}
])
print(" ✅ usuarios creada")
# Colección 2: sesiones
sesiones = db_usuarios['sesiones']
sesiones.insert_one({
"usuario_id": 1,
"token": "abc123xyz789",
"inicio": datetime.now(),
"activa": True,
"ip": "192.168.1.100"
})
print(" ✅ sesiones creada")
# Colección 3: preferencias
preferencias = db_usuarios['preferencias']
preferencias.insert_one({
"usuario_id": 1,
"notificaciones_email": True,
"tema": "oscuro",
"idioma": "es"
})
print(" ✅ preferencias creada")
# ========================================
# 5. BASE DE DATOS 4: biblioteca_estadisticas
# ========================================
print("\n📁 Creando Base de Datos: biblioteca_estadisticas")
db_estadisticas = client['biblioteca_estadisticas']
# Colección 1: visitas
visitas = db_estadisticas['visitas']
visitas.insert_many([
{
"fecha": datetime(2026, 1, 29),
"total_visitas": 120,
"paginas_vistas": 450,
"usuarios_unicos": 85
},
{
"fecha": datetime(2026, 1, 30),
"total_visitas": 135,
"paginas_vistas": 480,
"usuarios_unicos": 95
}
])
print(" ✅ visitas creada")
# Colección 2: prestamos (estadísticas de préstamos)
prestamos_stats = db_estadisticas['prestamos']
prestamos_stats.insert_many([
{
"fecha": datetime(2026, 1, 30),
"libro_id": 1, # ← CAMPO COMÚN
"total_prestamos": 15,
"promedio_dias_prestamo": 12
},
{
"fecha": datetime(2026, 1, 30),
"libro_id": 2,
"total_prestamos": 8,
"promedio_dias_prestamo": 10
}
])
print(" ✅ prestamos (stats) creada")
# Colección 3: tendencias
tendencias = db_estadisticas['tendencias']
tendencias.insert_one({
"periodo": "enero_2026",
"libros_mas_prestados": [
{"libro_id": 1, "cantidad": 15},
{"libro_id": 3, "cantidad": 12},
{"libro_id": 2, "cantidad": 8}
],
"categorias_populares": ["Realismo Mágico", "Distopía"]
})
print(" ✅ tendencias creada")
# ========================================
# 6. RESUMEN FINAL
# ========================================
print("\n" + "="*60)
print("✅ INICIALIZACIÓN COMPLETADA")
print("="*60)
# Listar todas las bases de datos creadas
dbs = client.list_database_names()
print(f"\n📊 Bases de datos en el cluster: {len(dbs)}")
for db in dbs:
if 'biblioteca' in db:
collections = client[db].list_collection_names()
print(f" 📁 {db}")
for col in collections:
count = client[db][col].count_documents({})
print(f" ├── {col} ({count} documentos)")
# Calcular uso de almacenamiento
print("\n💾 Uso de Almacenamiento:")
total_size = 0
for db_name in ['biblioteca_logs', 'biblioteca_catalogo',
'biblioteca_usuarios', 'biblioteca_estadisticas']:
stats = client[db_name].command("dbStats")
size_mb = stats['dataSize'] / (1024 * 1024)
total_size += size_mb
print(f" 📊 {db_name}: {size_mb:.2f} MB")
print(f"\n 🎯 Total usado: {total_size:.2f} MB / 512 MB ({(total_size/512)*100:.1f}%)")
client.close()
print("\n🔒 Conexión cerrada")
# Asegúrate de estar en la carpeta del proyecto
cd C:\PRADODIAZ\biblioteca_project
# Ejecuta el script
python init_mongodb.py
Salida esperada:
🔌 Conectando a MongoDB Atlas...
✅ Conexión exitosa a MongoDB Atlas
📁 Creando Base de Datos: biblioteca_logs
✅ logs_actividad creada (2 documentos)
✅ logs_errores creada
✅ logs_auditoria creada
📁 Creando Base de Datos: biblioteca_catalogo
✅ libros creada (3 documentos)
✅ autores creada
✅ categorias creada
✅ reseñas creada
📁 Creando Base de Datos: biblioteca_usuarios
✅ usuarios creada
✅ sesiones creada
✅ preferencias creada
📁 Creando Base de Datos: biblioteca_estadisticas
✅ visitas creada
✅ prestamos (stats) creada
✅ tendencias creada
============================================================
✅ INICIALIZACIÓN COMPLETADA
============================================================
📊 Bases de datos en el cluster: 7
📁 biblioteca_logs
├── logs_actividad (2 documentos)
├── logs_errores (1 documentos)
└── logs_auditoria (1 documentos)
📁 biblioteca_catalogo
├── libros (3 documentos)
├── autores (2 documentos)
├── categorias (3 documentos)
└── reseñas (2 documentos)
📁 biblioteca_usuarios
├── usuarios (1 documentos)
├── sesiones (1 documentos)
└── preferencias (1 documentos)
📁 biblioteca_estadisticas
├── visitas (2 documentos)
├── prestamos (2 documentos)
└── tendencias (1 documentos)
💾 Uso de Almacenamiento:
📊 biblioteca_logs: 0.03 MB
📊 biblioteca_catalogo: 0.05 MB
📊 biblioteca_usuarios: 0.02 MB
📊 biblioteca_estadisticas: 0.02 MB
🎯 Total usado: 0.12 MB / 512 MB (0.0%)
🔒 Conexión cerrada
- 🌐 Ve a cloud.mongodb.com
- 🔍 Haz clic en "Browse Collections" en tu cluster
- 👁️ Deberías ver las 4 bases de datos con todas sus colecciones:
- 📁 biblioteca_logs → 3 colecciones
- 📁 biblioteca_catalogo → 4 colecciones
- 📁 biblioteca_usuarios → 3 colecciones
- 📁 biblioteca_estadisticas → 3 colecciones
- ✅ Haz clic en
biblioteca_catalogo > libros - 👀 Verás los 3 libros con el campo libro_id (1, 2, 3)
✅ CHECKPOINT 6: MongoDB Atlas completamente configurado
En este punto tienes:
- ✅ 4 bases de datos creadas en MongoDB Atlas
- ✅ 13 colecciones con datos de ejemplo
- ✅ Campo común
libro_iden colecciónlibros - ✅ Estructura lista para conectar con Django
🎉 ¡La parte de MongoDB Atlas está completa!
📍 Siguiente: Configurar Django + MySQL para el sistema híbrido
🐍 PARTE 2: DJANGO + MYSQL (SISTEMA HÍBRIDO)
🎯 Objetivo de Esta Sección:
Crear el proyecto Django que conectará MySQL (local) con MongoDB Atlas (nube) usando el campo común libro_id
| Base de Datos | ¿Qué Guarda? | Tecnología |
|---|---|---|
| MySQL (Local) |
- Tabla: biblioteca_prestamo- Campos: id, libro_id (INT), usuario_id, fecha_prestamo, fecha_devolucion, estado |
Django ORM (models.py) |
| MongoDB Atlas |
- Colección: libros- Campos: libro_id (INT), titulo, isbn, autor{}, categorias[], stock, precio |
PyMongo (queries directas) |
|
🔗 Campo Común: libro_id (INT) MySQL: prestamo.libro_id ↔ MongoDB: libro.libro_id |
||
📋 Paso 8: Configurar Proyecto Django con MySQL
# Crear carpeta del proyecto
mkdir C:\PRADODIAZ\biblioteca_project
cd C:\PRADODIAZ\biblioteca_project
# Crear entorno virtual
python -m venv venv
# Activar entorno virtual
.\venv\Scripts\Activate
# Instalar Django y dependencias
pip install django pymongo dnspython mysqlclient
# Crear proyecto Django
django-admin startproject biblioteca_project .
# Crear app
python manage.py startapp biblioteca
from pathlib import Path
import os
from pymongo import MongoClient
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-tu-secret-key-aqui'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'biblioteca', # ← Tu app
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'biblioteca_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'biblioteca_project.wsgi.application'
# ========================================
# CONFIGURACIÓN MYSQL (BASE DE DATOS LOCAL)
# ========================================
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'biblioteca_db', # Nombre de tu base de datos MySQL
'USER': 'root', # Usuario de MySQL
'PASSWORD': '', # Contraseña de MySQL (vacía en XAMPP por defecto)
'HOST': 'localhost',
'PORT': '3306',
}
}
# ========================================
# CONFIGURACIÓN MONGODB ATLAS (NUBE)
# ========================================
# ⚠️ IMPORTANTE: REEMPLAZA CON TU CONNECTION STRING REAL DE MONGODB ATLAS
# Obtén tu string desde: MongoDB Atlas → Connect → Drivers
# Debe verse así (con TU cluster real):
# mongodb+srv://usuario:password@cluster0.ab12cd.mongodb.net/
MONGODB_URI = "mongodb+srv://biblioteca_admin:TuPasswordReal@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority"
# EJEMPLO REAL (cambia cluster0.ab12cd por tu cluster):
# MONGODB_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@cluster0.ab12cd.mongodb.net/?retryWrites=true&w=majority"
# Crear cliente MongoDB global
MONGO_CLIENT = MongoClient(MONGODB_URI)
# Diccionario de bases de datos de MongoDB
MONGODB_DATABASES = {
'logs': MONGO_CLIENT['biblioteca_logs'],
'catalogo': MONGO_CLIENT['biblioteca_catalogo'],
'usuarios': MONGO_CLIENT['biblioteca_usuarios'],
'estadisticas': MONGO_CLIENT['biblioteca_estadisticas'],
}
# Password validation
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
# Internationalization
LANGUAGE_CODE = 'es-mx'
TIME_ZONE = 'America/Mexico_City'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
⚠️ ERROR COMÚN: ConfigurationError - DNS query name does not exist
Si ves este error:
pymongo.errors.ConfigurationError: The DNS query name does not exist:
_mongodb._tcp.bibliotecacluster.xxxxx.mongodb.net.
🔴 CAUSA: Estás usando el connection string de EJEMPLO con .xxxxx. en lugar de tu cluster real.
✅ SOLUCIÓN - Obtener tu Connection String Real:
- Ve a MongoDB Atlas: https://cloud.mongodb.com
- Selecciona tu proyecto (ejemplo: "BibliotecaProject")
- Haz clic en "Connect" en tu cluster (Cluster0)
- Selecciona "Drivers"
- Copia el Connection String que aparece (similar a):
Ejemplo de Connection String Real
mongodb+srv://biblioteca_admin:<password>@cluster0.ab12cd.mongodb.net/?retryWrites=true&w=majorityNota: El código
ab12cdes único para tu cluster. NO uses xxxxx - Reemplaza
<password>con tu contraseña real (ejemplo:Biblioteca2026!)
📝 Ejemplo de Configuración Correcta:
# ❌ INCORRECTO (con xxxxx):
MONGODB_URI = "mongodb+srv://user:pass@cluster.xxxxx.mongodb.net/"
# ✅ CORRECTO (con tu código real):
MONGODB_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@cluster0.ab12cd.mongodb.net/?retryWrites=true&w=majority"
# Donde 'ab12cd' es el código único de TU cluster
💡 TIP: Si olvidaste tu contraseña, puedes resetearla desde:
Database Access → Edit User → Edit Password
Antes de continuar, es importante verificar que tu connection string funciona correctamente.
# Crear archivo test_mongo.py en la raíz del proyecto
notepad test_mongo.py
from pymongo import MongoClient
# ⚠️ IMPORTANTE: Pega TU connection string real aquí
# Obtén desde: MongoDB Atlas → Connect → Drivers
MONGODB_URI = "mongodb+srv://biblioteca_admin:TuPassword@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority"
print("🔄 Intentando conectar a MongoDB Atlas...")
print(f"📡 URI: {MONGODB_URI[:50]}...")
print("-" * 60)
try:
# Intentar conectar al cluster
client = MongoClient(MONGODB_URI, serverSelectionTimeoutMS=5000)
# Forzar la conexión con un comando
client.admin.command('ping')
print("✅ ¡CONEXIÓN EXITOSA!")
print("-" * 60)
# Listar todas las bases de datos disponibles
databases = client.list_database_names()
print(f"📚 Bases de datos encontradas ({len(databases)}):")
for db in databases:
print(f" - {db}")
print("-" * 60)
# Verificar las 4 bases de datos del proyecto (si ya existen)
expected_dbs = [
"biblioteca_logs",
"biblioteca_catalogo",
"biblioteca_usuarios",
"biblioteca_estadisticas"
]
print("🔍 Verificando bases de datos del proyecto:")
for db_name in expected_dbs:
if db_name in databases:
# Contar colecciones
db = client[db_name]
collections = db.list_collection_names()
print(f" ✅ {db_name}: {len(collections)} colecciones")
if collections:
for col in collections:
count = db[col].count_documents({})
print(f" → {col}: {count} documentos")
else:
print(f" ⚠️ {db_name}: No existe (se creará con init_mongodb.py)")
print("-" * 60)
print("✅ Prueba completada exitosamente")
print("👉 Puedes continuar con las migraciones de Django")
except Exception as e:
print("❌ ERROR DE CONEXIÓN:")
print("-" * 60)
print(str(e))
print("-" * 60)
print("\n🔧 SOLUCIONES POSIBLES:")
print("1. Verifica que el connection string sea correcto")
print("2. Reemplaza 'xxxxx' con el código de tu cluster")
print("3. Reemplaza '<password>' con tu contraseña real")
print("4. Verifica que tu IP esté en la lista blanca (0.0.0.0/0)")
print("5. Verifica que el usuario tenga permisos de lectura/escritura")
📝 Cómo usar este script:
- Crea el archivo:
PowerShell
# Asegúrate de estar en la raíz del proyecto cd C:\PRADODIAZ\biblioteca_project # Crear el archivo test_mongo.py notepad test_mongo.py - Copia y pega el código completo del script (mostrado arriba) en el archivo
- ⚠️ IMPORTANTE: Reemplaza el
MONGODB_URIcon tu connection string real:test_mongo.py (línea 5 - EDITAR ESTO)# ❌ NO USES ESTO (es un ejemplo): MONGODB_URI = "mongodb+srv://biblioteca_admin:TuPassword@cluster0.xxxxx.mongodb.net/..." # ✅ USA TU CONNECTION STRING REAL: # Ve a MongoDB Atlas → Connect → Drivers → Copia tu string MONGODB_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@cluster0.ab12cd.mongodb.net/?retryWrites=true&w=majority" # ^^^^^^ # Reemplaza con tu código real - Guarda el archivo (Ctrl + S en Notepad)
- Cierra Notepad y ejecuta el script desde PowerShell
# Ejecutar el script de prueba
python test_mongo.py
✅ Resultado Esperado (Conexión Exitosa):
🔄 Intentando conectar a MongoDB Atlas...
📡 URI: mongodb+srv://biblioteca_admin:***@cluster0.ab12...
------------------------------------------------------------
✅ ¡CONEXIÓN EXITOSA!
------------------------------------------------------------
📚 Bases de datos encontradas (3):
- admin
- local
- sample_mflix
------------------------------------------------------------
🔍 Verificando bases de datos del proyecto:
⚠️ biblioteca_logs: No existe (se creará con init_mongodb.py)
⚠️ biblioteca_catalogo: No existe (se creará con init_mongodb.py)
⚠️ biblioteca_usuarios: No existe (se creará con init_mongodb.py)
⚠️ biblioteca_estadisticas: No existe (se creará con init_mongodb.py)
------------------------------------------------------------
✅ Prueba completada exitosamente
👉 Puedes continuar con las migraciones de Django
💡 Nota: Es normal que las bases de datos del proyecto no existan aún. Se crearán cuando ejecutes init_mongodb.py más adelante.
❌ Si obtienes error de conexión:
Revisa estos puntos:
- Connection String incorrecto: Verifica que no contenga
xxxxx - Contraseña incorrecta: Usa la contraseña que configuraste en Database Access
- IP no permitida: En MongoDB Atlas → Network Access → Add IP Address → Allow Access from Anywhere (0.0.0.0/0)
- Usuario sin permisos: En Database Access → Edit User → Built-in Role: Atlas admin
- Firewall/Antivirus: Puede estar bloqueando la conexión al puerto 27017
-- Abre phpMyAdmin (http://localhost/phpmyadmin/)
-- O usa la consola MySQL:
mysql -u root -p
-- Crear base de datos:
CREATE DATABASE biblioteca_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Verificar:
SHOW DATABASES;
-- Salir:
EXIT;
from django.db import models
from django.contrib.auth.models import User
class Prestamo(models.Model):
"""
Modelo para préstamos (se guarda en MySQL)
⚠️ IMPORTANTE: libro_id es el campo común con MongoDB
"""
# Campo común con MongoDB (INT)
libro_id = models.IntegerField(
verbose_name="ID del Libro",
help_text="ID del libro en MongoDB (colección libros)"
)
# Relación con usuario de Django
usuario = models.ForeignKey(
User,
on_delete=models.CASCADE,
verbose_name="Usuario"
)
# Fechas del préstamo
fecha_prestamo = models.DateTimeField(
auto_now_add=True,
verbose_name="Fecha de Préstamo"
)
fecha_devolucion = models.DateTimeField(
null=True,
blank=True,
verbose_name="Fecha de Devolución"
)
# Estado del préstamo
ESTADOS = [
('ACTIVO', 'Activo'),
('DEVUELTO', 'Devuelto'),
('VENCIDO', 'Vencido'),
]
estado = models.CharField(
max_length=10,
choices=ESTADOS,
default='ACTIVO',
verbose_name="Estado"
)
# Días de préstamo (se calculará desde MongoDB)
dias_prestamo = models.IntegerField(
default=14,
verbose_name="Días de Préstamo"
)
class Meta:
db_table = 'biblioteca_prestamo'
verbose_name = 'Préstamo'
verbose_name_plural = 'Préstamos'
ordering = ['-fecha_prestamo']
def __str__(self):
return f"Préstamo #{self.id} - Libro ID: {self.libro_id}"
class Multa(models.Model):
"""Modelo para multas por retraso en devolución"""
prestamo = models.ForeignKey(
Prestamo,
on_delete=models.CASCADE,
verbose_name="Préstamo"
)
monto = models.DecimalField(
max_digits=6,
decimal_places=2,
verbose_name="Monto"
)
pagada = models.BooleanField(
default=False,
verbose_name="Pagada"
)
fecha_creacion = models.DateTimeField(
auto_now_add=True,
verbose_name="Fecha de Creación"
)
class Meta:
db_table = 'biblioteca_multa'
verbose_name = 'Multa'
verbose_name_plural = 'Multas'
def __str__(self):
return f"Multa ${self.monto} - Préstamo #{self.prestamo.id}"
⚠️ ERROR COMÚN: NameError: name 'BASE_DIR' is not defined
Si ves este error al ejecutar migraciones:
File "settings.py", line 38, in <module>
'DIRS': [os.path.join(BASE_DIR, 'templates')],
^^^^^^^^
NameError: name 'BASE_DIR' is not defined
✅ SOLUCIÓN: Agrega estas líneas al INICIO de settings.py:
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Explicación:
BASE_DIRes la ruta base del proyecto Django- Se usa para definir rutas absolutas (templates, static files, etc.)
- Debe estar definido ANTES de usarlo en
TEMPLATES
# Crear archivos de migración
python manage.py makemigrations
# Aplicar migraciones a MySQL
python manage.py migrate
# Crear superusuario
python manage.py createsuperuser
# Username: admin
# Email: admin@biblioteca.com
# Password: admin123
Resultado esperado en MySQL:
- ✅ Tabla
biblioteca_prestamocreada - ✅ Tabla
biblioteca_multacreada - ✅ Tablas de Django (auth_user, etc.) creadas
✅ CHECKPOINT 7: MySQL configurado
En este punto tienes:
- ✅ Proyecto Django creado
- ✅ MySQL configurado con base de datos
biblioteca_db - ✅ Modelos
PrestamoyMultacon campolibro_id - ✅ MongoDB configurado en settings.py
- ✅ Listo para crear views que usen ambas BDs
📋 Paso 7: Crear Views Híbridas (MySQL + MongoDB)
🎯 Views que Conectan Ambas Bases de Datos
Ahora crearemos views que usan el campo libro_id para combinar datos de MySQL y MongoDB
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.conf import settings
from django.utils import timezone
from .models import Prestamo, Multa
from datetime import datetime, timedelta
# ========================================
# 1. LISTAR LIBROS (Solo MongoDB)
# ========================================
def listar_libros(request):
"""
Lista todos los libros desde MongoDB Atlas
"""
# Obtener base de datos de MongoDB desde settings
db_catalogo = settings.MONGODB_DATABASES['catalogo']
# Buscar parámetro de búsqueda
query = request.GET.get('q', '')
if query:
# Búsqueda con regex (case-insensitive)
libros = db_catalogo.libros.find({
"$or": [
{"titulo": {"$regex": query, "$options": "i"}},
{"autor.nombre": {"$regex": query, "$options": "i"}},
]
})
else:
# Obtener todos los libros
libros = db_catalogo.libros.find()
# Convertir cursor a lista
libros_list = list(libros)
context = {
'libros': libros_list,
'query': query,
'total_libros': len(libros_list)
}
return render(request, 'biblioteca/listar_libros.html', context)
# ========================================
# 2. DETALLE DE LIBRO (MongoDB + MySQL)
# ========================================
def detalle_libro(request, libro_id):
"""
Muestra detalle de un libro combinando datos de:
- MongoDB: Información del libro (titulo, autor, etc.)
- MySQL: Préstamos activos de este libro
"""
# 1. Obtener información del libro desde MongoDB
db_catalogo = settings.MONGODB_DATABASES['catalogo']
libro = db_catalogo.libros.find_one({"libro_id": libro_id})
if not libro:
messages.error(request, "Libro no encontrado")
return redirect('listar_libros')
# 2. Obtener préstamos activos de este libro desde MySQL
prestamos_activos = Prestamo.objects.filter(
libro_id=libro_id,
estado='ACTIVO'
).select_related('usuario')
# 3. Obtener reseñas desde MongoDB
reseñas = db_catalogo.reseñas.find({"libro_id": libro_id})
context = {
'libro': libro, # MongoDB
'prestamos_activos': prestamos_activos, # MySQL
'reseñas': list(reseñas), # MongoDB
'disponible': libro.get('stock', 0) > 0
}
return render(request, 'biblioteca/detalle_libro.html', context)
# ========================================
# 3. PRESTAR LIBRO (MySQL + MongoDB)
# ========================================
@login_required
def prestar_libro(request, libro_id):
"""
Crea un préstamo en MySQL y actualiza stock en MongoDB
⚠️ Esta es la operación crítica del sistema híbrido
"""
if request.method == 'POST':
db_catalogo = settings.MONGODB_DATABASES['catalogo']
db_logs = settings.MONGODB_DATABASES['logs']
db_estadisticas = settings.MONGODB_DATABASES['estadisticas']
# 1. Buscar libro en MongoDB
libro = db_catalogo.libros.find_one({"libro_id": libro_id})
if not libro:
messages.error(request, "Libro no encontrado")
return redirect('listar_libros')
# 2. Verificar stock disponible (MongoDB)
if libro.get('stock', 0) <= 0:
messages.error(request, f"El libro '{libro['titulo']}' no tiene stock disponible")
return redirect('detalle_libro', libro_id=libro_id)
# 3. Verificar que el usuario no tenga préstamos activos del mismo libro
prestamo_existente = Prestamo.objects.filter(
libro_id=libro_id,
usuario=request.user,
estado='ACTIVO'
).exists()
if prestamo_existente:
messages.warning(request, "Ya tienes un préstamo activo de este libro")
return redirect('detalle_libro', libro_id=libro_id)
try:
# 4. Crear préstamo en MySQL
prestamo = Prestamo.objects.create(
libro_id=libro_id, # ← CAMPO COMÚN
usuario=request.user,
dias_prestamo=14
)
# 5. Actualizar stock en MongoDB
db_catalogo.libros.update_one(
{"libro_id": libro_id},
{"$inc": {"stock": -1}} # Restar 1 al stock
)
# 6. Registrar log en MongoDB
db_logs.logs_actividad.insert_one({
"timestamp": datetime.now(),
"usuario": request.user.username,
"accion": "PRESTAMO_CREADO",
"libro_id": libro_id, # ← CAMPO COMÚN
"prestamo_id": prestamo.id,
"detalles": f"Préstamo del libro '{libro['titulo']}'"
})
# 7. Actualizar estadísticas en MongoDB
db_estadisticas.prestamos.insert_one({
"fecha": datetime.now(),
"libro_id": libro_id, # ← CAMPO COMÚN
"usuario": request.user.username,
"accion": "PRESTADO"
})
messages.success(
request,
f"✅ Préstamo registrado exitosamente. Fecha de devolución: "
f"{(timezone.now() + timedelta(days=14)).strftime('%d/%m/%Y')}"
)
return redirect('mis_prestamos')
except Exception as e:
# Si hay error, registrar en logs de MongoDB
db_logs.logs_errores.insert_one({
"timestamp": datetime.now(),
"nivel": "ERROR",
"modulo": "prestamos",
"mensaje": str(e),
"usuario": request.user.username,
"libro_id": libro_id
})
messages.error(request, f"Error al crear préstamo: {str(e)}")
return redirect('detalle_libro', libro_id=libro_id)
return redirect('detalle_libro', libro_id=libro_id)
# ========================================
# 4. DEVOLVER LIBRO (MySQL + MongoDB)
# ========================================
@login_required
def devolver_libro(request, prestamo_id):
"""
Marca préstamo como devuelto en MySQL y actualiza stock en MongoDB
"""
prestamo = get_object_or_404(Prestamo, id=prestamo_id, usuario=request.user)
if prestamo.estado != 'ACTIVO':
messages.warning(request, "Este préstamo ya fue devuelto")
return redirect('mis_prestamos')
db_catalogo = settings.MONGODB_DATABASES['catalogo']
db_logs = settings.MONGODB_DATABASES['logs']
try:
# 1. Actualizar préstamo en MySQL
prestamo.estado = 'DEVUELTO'
prestamo.fecha_devolucion = timezone.now()
prestamo.save()
# 2. Restaurar stock en MongoDB
db_catalogo.libros.update_one(
{"libro_id": prestamo.libro_id}, # ← CAMPO COMÚN
{"$inc": {"stock": 1}} # Sumar 1 al stock
)
# 3. Registrar devolución en logs
db_logs.logs_actividad.insert_one({
"timestamp": datetime.now(),
"usuario": request.user.username,
"accion": "DEVOLUCION",
"libro_id": prestamo.libro_id, # ← CAMPO COMÚN
"prestamo_id": prestamo.id
})
messages.success(request, "✅ Libro devuelto exitosamente")
except Exception as e:
messages.error(request, f"Error al devolver libro: {str(e)}")
return redirect('mis_prestamos')
# ========================================
# 5. MIS PRÉSTAMOS (Consulta Híbrida)
# ========================================
@login_required
def mis_prestamos(request):
"""
Lista préstamos del usuario combinando datos de MySQL y MongoDB
"""
# 1. Obtener préstamos del usuario desde MySQL
prestamos = Prestamo.objects.filter(usuario=request.user).order_by('-fecha_prestamo')
# 2. Obtener base de datos MongoDB
db_catalogo = settings.MONGODB_DATABASES['catalogo']
# 3. Para cada préstamo, obtener datos del libro desde MongoDB
prestamos_completos = []
for prestamo in prestamos:
# Buscar libro en MongoDB usando el campo común
libro = db_catalogo.libros.find_one({"libro_id": prestamo.libro_id})
# Combinar datos
prestamos_completos.append({
'prestamo': prestamo, # MySQL
'libro': libro if libro else {'titulo': 'Libro no encontrado'} # MongoDB
})
context = {
'prestamos_completos': prestamos_completos,
'total_prestamos': prestamos.count()
}
return render(request, 'biblioteca/mis_prestamos.html', context)
# ========================================
# 6. DASHBOARD (Estadísticas Híbridas)
# ========================================
def dashboard(request):
"""
Dashboard con estadísticas de MySQL y MongoDB
"""
db_catalogo = settings.MONGODB_DATABASES['catalogo']
db_estadisticas = settings.MONGODB_DATABASES['estadisticas']
# Estadísticas de MySQL
total_prestamos_activos = Prestamo.objects.filter(estado='ACTIVO').count()
total_prestamos_historico = Prestamo.objects.count()
# Estadísticas de MongoDB
total_libros = db_catalogo.libros.count_documents({})
libros_disponibles = db_catalogo.libros.count_documents({"disponible": True})
# Libros más prestados (MongoDB estadísticas)
tendencia = db_estadisticas.tendencias.find_one(
{"periodo": "enero_2026"}
)
context = {
'total_prestamos_activos': total_prestamos_activos,
'total_prestamos_historico': total_prestamos_historico,
'total_libros': total_libros,
'libros_disponibles': libros_disponibles,
'tendencia': tendencia
}
return render(request, 'biblioteca/dashboard.html', context)
🔍 EXPLICACIÓN DEL FLUJO HÍBRIDO:
Cuando un usuario pide un libro prestado:
- MySQL: Crea registro en tabla
biblioteca_prestamoconlibro_id=5 - MongoDB: Busca libro con
{"libro_id": 5}en colecciónlibros - MongoDB: Actualiza
stock: -1del libro - MongoDB: Registra log en colección
logs_actividad - MongoDB: Registra estadística en colección
prestamos
Cuando lista "Mis Préstamos":
- MySQL: Obtiene préstamos con
Prestamo.objects.filter(usuario=user) - Para cada préstamo:
- Obtiene
libro_iddel préstamo (MySQL) - Busca datos del libro en MongoDB con ese
libro_id - Combina: fecha_prestamo (MySQL) + titulo/autor (MongoDB)
- Obtiene
📋 Paso 8: Configurar URLs
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('biblioteca.urls')), # URLs de la app
]
from django.urls import path
from . import views
urlpatterns = [
# Dashboard
path('', views.dashboard, name='dashboard'),
# Libros (MongoDB)
path('libros/', views.listar_libros, name='listar_libros'),
path('libros/<int:libro_id>/', views.detalle_libro, name='detalle_libro'),
# Préstamos (MySQL + MongoDB)
path('prestamos/crear/<int:libro_id>/', views.prestar_libro, name='prestar_libro'),
path('prestamos/devolver/<int:prestamo_id>/', views.devolver_libro, name='devolver_libro'),
path('prestamos/mis-prestamos/', views.mis_prestamos, name='mis_prestamos'),
]
📋 Paso 9: Crear Templates HTML
# Desde la carpeta del proyecto
mkdir biblioteca\templates\biblioteca
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Biblioteca - Sistema Híbrido{% endblock %}</title>
<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.container {
background: white;
border-radius: 15px;
padding: 30px;
margin-top: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.badge-mysql {
background: #00758f;
}
.badge-mongodb {
background: #00ed64;
color: #000;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark" style="background: rgba(0,0,0,0.3);">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'dashboard' %}">
<i class="bi bi-book"></i> Biblioteca Híbrida
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'dashboard' %}">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'listar_libros' %}">
<span class="badge badge-mongodb">MongoDB</span> Libros
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'mis_prestamos' %}">
<span class="badge badge-mysql">MySQL</span> Mis Préstamos
</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<span class="nav-link">👤 {{ user.username }}</span>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Mensajes -->
<div class="container mt-3">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
</div>
<!-- Contenido -->
<div class="container">
{% block content %}{% endblock %}
</div>
<!-- Footer -->
<footer class="text-center text-white mt-5 pb-3">
<p>
<span class="badge badge-mysql">MySQL</span> +
<span class="badge badge-mongodb">MongoDB Atlas</span>
Sistema Híbrido 2026
</p>
</footer>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
⚠️ IMPORTANTE: Templates Completos en Archivo Separado
El código del template base.html mostrado arriba es solo una vista previa.
Para ver y copiar el código completo de TODOS los templates necesarios para el sistema híbrido, consulta el archivo:
📄 Archivo de Templates Completos:
📂 Ubicación: En la misma carpeta que este archivo
📋 Contenido del archivo:
├── ✅ base.html (Template base con navbar y estilos)
├── ✅ dashboard.html (Vista principal con estadísticas)
├── ✅ listar_libros.html (Catálogo de MongoDB)
└── ✅ mis_prestamos.html (Vista híbrida MySQL + MongoDB)
🎯 Total: 4 templates completos listos para copiar y usar
📝 Instrucciones:
- Abre el archivo:
MONGODB_VIEWS_TEMPLATES_COMPLETOS.html - Busca cada template por su nombre (base.html, dashboard.html, etc.)
- Copia el código completo de cada template
- Pégalo en tu proyecto Django en la carpeta correspondiente:
Estructura de carpetas
biblioteca_project/ ├── biblioteca/ │ └── templates/ │ └── biblioteca/ │ ├── base.html ← Copiar aquí │ ├── dashboard.html ← Copiar aquí │ ├── listar_libros.html ← Copiar aquí │ └── mis_prestamos.html ← Copiar aquí - Verifica que todos los archivos estén creados correctamente
⚠️ NOTA: No omitas este paso. Los templates son esenciales para que el sistema funcione correctamente.
✅ CHECKPOINT 8: Sistema híbrido completo
🎉 ¡FELICIDADES! Has completado el sistema híbrido:
- ✅ MongoDB Atlas: 4 bases de datos con 13 colecciones
- ✅ MySQL: Tabla prestamos con campo libro_id
- ✅ Django Views: 6 views que conectan ambas BDs
- ✅ URLs: Configuradas correctamente
- ✅ Templates: Base template con Bootstrap 5
- ✅ Campo común: libro_id conecta MySQL ↔ MongoDB
🚀 Para ejecutar el proyecto:
python manage.py runserver
# Abre en el navegador:
# http://localhost:8000/
# Verás:
# - Dashboard con estadísticas de ambas BDs
# - Lista de libros desde MongoDB
# - Sistema de préstamos usando MySQL + MongoDB
4. 💻 Saber Hacer - Implementación con Django
Paso 1: Instalar Dependencias
⏱️ FASE 3: Instalación de Dependencias Python (20 minutos)
Objetivo: Instalar PyMongo, Djongo, dnspython y verificar instalación
# Navega a tu carpeta de proyecto (ApellidoPaternoApellidoMaterno):
cd C:\PRADODIAZ\biblioteca_project
# Crea entorno virtual:
python -m venv venv
# Activa el entorno virtual:
.\venv\Scripts\Activate
# Deberías ver (venv) al inicio del prompt:
# (venv) PS C:\PRADODIAZ\biblioteca_project>
pip install pymongo==4.6.0
# Deberías ver al final:
# Successfully installed pymongo-4.6.0
✅ Verificar instalación:
python -c "import pymongo; print('✅ PyMongo version:', pymongo.version)"
# Debe mostrar: ✅ PyMongo version: 4.6.0
pip install dnspython==2.4.2
# También instala el soporte SRV completo:
pip install "pymongo[srv]"
# Deberías ver:
# Successfully installed dnspython-2.4.2
✅ Verificar instalación:
python -c "import dns; print('✅ dnspython instalado correctamente')"
# Debe mostrar: ✅ dnspython instalado correctamente
# Nota: Djongo puede tener problemas de compatibilidad con Django 4.x
# Solo instálalo si quieres usar modelos Django con MongoDB
pip install djongo==1.3.6
# Alternativa moderna (usa PyMongo directamente):
# En lugar de djongo, puedes usar PyMongo en las views directamente
⚠️ Problemas conocidos con Djongo:
- ❌ Djongo 1.3.6 tiene bugs con Django 4.x
- ✅ Alternativa recomendada: Usa PyMongo directamente en las views
- ✅ Ventaja: Más control, menos bugs, mejor rendimiento
- 📖 En esta guía usaremos PyMongo directo (más estable)
pip install djangorestframework==3.14.0
# Deberías ver:
# Successfully installed djangorestframework-3.14.0
pip freeze > requirements.txt
# Verifica el contenido:
cat requirements.txt
# Deberías ver (entre otras):
# Django==4.2.x
# pymongo==4.6.0
# dnspython==2.4.2
# djangorestframework==3.14.0
✅ CHECKPOINT 5: Dependencias instaladas correctamente
Verifica que TODO esté instalado con este comando:
pip list | Select-String "pymongo|dnspython|django|djangorestframework"
# Deberías ver:
# Django 4.2.x
# djangorestframework 3.14.0
# dnspython 2.4.2
# pymongo 4.6.0
✅ Si ves TODAS esas líneas: ¡Perfecto! Continúa al siguiente paso
❌ Si falta alguna: Vuelve atrás e instálala con pip install [paquete]
🧪 TEST: Probar conexión a MongoDB Atlas
Antes de continuar con Django, verifica que puedes conectarte a Atlas:
# Crea un archivo test_connection.py en la raíz del proyecto:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
import sys
# ⚠️ REEMPLAZA con tu connection string real:
MONGO_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/biblioteca_logs?retryWrites=true&w=majority"
print("🔍 Intentando conectar a MongoDB Atlas...")
print(f"📡 URI: {MONGO_URI[:50]}...")
try:
# Crear cliente con timeout de 5 segundos
client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
# Forzar conexión (lazy connection por defecto)
server_info = client.server_info()
print("✅ ¡CONEXIÓN EXITOSA!")
print(f"📊 MongoDB Version: {server_info['version']}")
print(f"🗄️ Bases de datos: {client.list_database_names()}")
# Probar escritura
db = client['biblioteca_logs']
test_collection = db['test']
result = test_collection.insert_one({'test': 'Conexión exitosa', 'timestamp': '2026-01-20'})
print(f"✅ Documento de prueba insertado con ID: {result.inserted_id}")
# Limpiar test
test_collection.delete_one({'_id': result.inserted_id})
print("🧹 Documento de prueba eliminado")
print("\n🎉 ¡Todo funciona correctamente! Puedes continuar con Django.")
except ConnectionFailure as e:
print(f"❌ ERROR DE CONEXIÓN: {e}")
print("\n🔧 POSIBLES SOLUCIONES:")
print(" 1. Verifica tu connection string (usuario, contraseña)")
print(" 2. Revisa Network Access en Atlas (IP whitelist)")
print(" 3. Verifica que el cluster esté 'Active' (no 'Paused')")
sys.exit(1)
except Exception as e:
print(f"❌ ERROR: {e}")
sys.exit(1)
finally:
client.close()
print("🔌 Conexión cerrada")
Ejecuta el test:
python test_connection.py
# Salida esperada:
# 🔍 Intentando conectar a MongoDB Atlas...
# ✅ ¡CONEXIÓN EXITOSA!
# 📊 MongoDB Version: 7.0.5
# 🗄️ Bases de datos: ['admin', 'biblioteca_logs', 'local']
# ✅ Documento de prueba insertado con ID: 65a1b2c3d4e5f6789...
# 🧹 Documento de prueba eliminado
# 🎉 ¡Todo funciona correctamente! Puedes continuar con Django.
# Si solo quieres copiar y pegar todo de una vez:
.\venv\Scripts\Activate
pip install pymongo==4.6.0
pip install dnspython==2.4.2
pip install "pymongo[srv]"
pip install djangorestframework==3.14.0
pip freeze > requirements.txt
# Verificar:
pip list | Select-String "pymongo|dnspython|django"
Paso 2: Configurar Django con MongoDB
import os # Para manejar variables de entorno
# Configuración de múltiples bases de datos (MySQL + MongoDB)
DATABASES = {
'default': { # Base de datos principal (MySQL)
'ENGINE': 'django.db.backends.mysql', # Motor de MySQL
'NAME': 'biblioteca_db', # Nombre de la base de datos MySQL
'USER': 'root', # Usuario de MySQL
'PASSWORD': 'password', # Contraseña de MySQL
'HOST': 'localhost', # Host de MySQL (local)
'PORT': '3306', # Puerto por defecto de MySQL
},
'mongodb': { # Base de datos secundaria (MongoDB Atlas)
'ENGINE': 'djongo', # Motor Djongo para MongoDB
'NAME': 'biblioteca_logs', # Nombre de la base de datos en MongoDB
'ENFORCE_SCHEMA': False, # Permite esquemas flexibles (NoSQL)
'CLIENT': {
'host': os.environ.get( # Obtiene connection string de variable de entorno
'MONGODB_URI',
'mongodb+srv://usuario:password@cluster.mongodb.net/biblioteca_logs?retryWrites=true&w=majority'
) # Connection string de Atlas (reemplazar con el tuyo)
}
}
}
# Router para distribuir modelos entre bases de datos
DATABASE_ROUTERS = ['libros.db_router.MongoDBRouter'] # Enrutador personalizado para decidir qué BD usar
# En producción, configurar variables de entorno:
# MONGODB_URI=mongodb+srv://usuario:password@cluster.mongodb.net/
5. 🚀 Proyecto: Sistema Híbrido MySQL + MongoDB
Arquitectura del Sistema
💡 Estrategia Híbrida:
- MySQL: Datos estructurados (Libros, Autores, Usuarios)
- MongoDB: Logs, Analytics, Datos no estructurados
Paso 1: Crear Modelos para MongoDB
from djongo import models # Importa models de djongo en lugar de django.db
from django.contrib.auth.models import User # Usuario de Django
class LogActividad(models.Model):
"""
Modelo para registrar actividad de usuarios en MongoDB
No requiere migraciones, es totalmente flexible
"""
usuario = models.CharField(max_length=100) # Nombre del usuario que realizó la acción
accion = models.CharField(max_length=50) # Tipo de acción (CREATE, UPDATE, DELETE, VIEW)
modelo = models.CharField(max_length=50) # Nombre del modelo afectado (Libro, Autor, etc)
objeto_id = models.IntegerField() # ID del objeto afectado
detalles = models.JSONField() # Información adicional en formato JSON flexible
timestamp = models.DateTimeField(auto_now_add=True) # Fecha y hora automática de creación
ip_address = models.GenericIPAddressField() # Dirección IP del usuario
user_agent = models.TextField(blank=True) # Información del navegador/cliente
class Meta:
db_table = 'logs_actividad' # Nombre de la colección en MongoDB
managed = False # Django no intentará crear migraciones para este modelo
def __str__(self):
return f"{self.usuario} - {self.accion} en {self.modelo} - {self.timestamp}"
class EstadisticaLibro(models.Model):
"""
Estadísticas de visualizaciones y préstamos de libros
Almacenadas en MongoDB para análisis
"""
libro_id = models.IntegerField() # ID del libro en MySQL
titulo = models.CharField(max_length=300) # Título del libro (desnormalizado para rapidez)
visualizaciones = models.IntegerField(default=0) # Cantidad de veces visto
prestamos = models.IntegerField(default=0) # Cantidad de veces prestado
calificacion_promedio = models.FloatField(default=0.0) # Promedio de calificaciones
# Datos de popularidad por mes (array de objetos)
estadisticas_mensuales = models.JSONField(default=list) # [{mes: 1, views: 50, prestamos: 10}, ...]
ultima_actualizacion = models.DateTimeField(auto_now=True) # Se actualiza automáticamente
class Meta:
db_table = 'estadisticas_libros' # Colección en MongoDB
managed = False # Sin migraciones
Paso 2: Crear Router de Bases de Datos
class MongoDBRouter:
"""
Router para dirigir ciertos modelos a MongoDB
y otros a MySQL (base de datos por defecto)
"""
# Lista de modelos que deben usar MongoDB
mongodb_models = ['LogActividad', 'EstadisticaLibro'] # Modelos que van a MongoDB
def db_for_read(self, model, **hints):
"""Determina qué base de datos usar para lectura"""
if model.__name__ in self.mongodb_models: # Si el modelo está en la lista
return 'mongodb' # Usa MongoDB para leer
return 'default' # Usa MySQL por defecto
def db_for_write(self, model, **hints):
"""Determina qué base de datos usar para escritura"""
if model.__name__ in self.mongodb_models:
return 'mongodb' # Escribe en MongoDB
return 'default' # Escribe en MySQL
def allow_relation(self, obj1, obj2, **hints):
"""Permite relaciones solo dentro de la misma BD"""
db1 = self.db_for_read(obj1.__class__) # Base de datos del objeto 1
db2 = self.db_for_read(obj2.__class__) # Base de datos del objeto 2
return db1 == db2 # Solo permite si están en la misma BD
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""Controla qué migraciones se aplican a cada BD"""
if db == 'mongodb':
return False # MongoDB no necesita migraciones
return True # MySQL sí necesita migraciones
6. 🔧 Operaciones CRUD con MongoDB
Usando Django ORM con Djongo
from libros.models import LogActividad, EstadisticaLibro
# ===== CREATE (Crear documento) =====
log = LogActividad.objects.create( # Crea y guarda en MongoDB
usuario='admin',
accion='CREATE',
modelo='Libro',
objeto_id=123,
detalles={'titulo': 'Cien Años de Soledad', 'autor': 'García Márquez'}, # JSON flexible
ip_address='192.168.1.1'
)
# ===== READ (Leer documentos) =====
# Obtener todos los logs
todos_logs = LogActividad.objects.all() # SELECT * equivalente
# Filtrar por usuario
logs_admin = LogActividad.objects.filter(usuario='admin') # WHERE usuario = 'admin'
# Filtrar por múltiples condiciones
logs_recientes = LogActividad.objects.filter(
accion='DELETE', # Acción específica
timestamp__gte='2026-01-01' # Mayor o igual a fecha (gte = greater than or equal)
).order_by('-timestamp') # Ordenar por timestamp descendente
# Obtener un documento por ID
log = LogActividad.objects.get(pk=log_id) # Busca por primary key (_id en MongoDB)
# ===== UPDATE (Actualizar) =====
# Actualizar un documento
log = LogActividad.objects.get(pk=log_id)
log.detalles['actualizado'] = True # Modifica el JSON
log.save() # Guarda cambios en MongoDB
# Actualizar múltiples documentos
LogActividad.objects.filter(
usuario='test'
).update( # UPDATE WHERE usuario = 'test'
user_agent='Updated'
)
# ===== DELETE (Eliminar) =====
# Eliminar un documento
log = LogActividad.objects.get(pk=log_id)
log.delete() # Elimina de MongoDB
# Eliminar múltiples documentos
LogActividad.objects.filter(
timestamp__lt='2025-01-01' # Menores a fecha (lt = less than)
).delete() # DELETE WHERE timestamp < '2025-01-01'
# ===== AGREGACIONES =====
from django.db.models import Count, Avg
# Contar logs por usuario
conteo = LogActividad.objects.values('usuario').annotate( # GROUP BY usuario
total=Count('id') # COUNT(*)
)
# Promedio de visualizaciones
promedio = EstadisticaLibro.objects.aggregate( # Agregación
Avg('visualizaciones') # AVG(visualizaciones)
)
Usando PyMongo Directamente
import pymongo
from django.conf import settings
from datetime import datetime
class MongoDBService:
"""Servicio para operaciones avanzadas con MongoDB"""
def __init__(self):
# Conectar a MongoDB Atlas
self.client = pymongo.MongoClient( # Crea cliente de MongoDB
settings.DATABASES['mongodb']['CLIENT']['host'] # Usa connection string de settings
)
self.db = self.client['biblioteca_logs'] # Selecciona base de datos
def registrar_actividad(self, usuario, accion, modelo, objeto_id, detalles, ip):
"""Registra una actividad del usuario"""
documento = { # Crea documento JSON
'usuario': usuario,
'accion': accion,
'modelo': modelo,
'objeto_id': objeto_id,
'detalles': detalles, # Puede ser cualquier estructura JSON
'timestamp': datetime.now(), # Fecha actual
'ip_address': ip
}
resultado = self.db.logs_actividad.insert_one(documento) # Inserta en colección
return str(resultado.inserted_id) # Retorna ID generado
def obtener_estadisticas_usuario(self, usuario):
"""Obtiene estadísticas agregadas de un usuario"""
pipeline = [ # Pipeline de agregación de MongoDB
{'$match': {'usuario': usuario}}, # Filtrar por usuario (WHERE)
{'$group': { # Agrupar (GROUP BY)
'_id': '$accion', # Agrupar por acción
'total': {'$sum': 1}, # Contar (COUNT)
'ultima_vez': {'$max': '$timestamp'} # Fecha más reciente (MAX)
}}
]
resultado = self.db.logs_actividad.aggregate(pipeline) # Ejecuta agregación
return list(resultado) # Convierte cursor a lista
def limpiar_logs_antiguos(self, dias=30):
"""Elimina logs más antiguos que X días"""
from datetime import timedelta
fecha_limite = datetime.now() - timedelta(days=dias) # Calcula fecha límite
resultado = self.db.logs_actividad.delete_many({ # Elimina múltiples documentos
'timestamp': {'$lt': fecha_limite} # Timestamp menor que fecha_limite
})
return resultado.deleted_count # Retorna cantidad eliminada
6.5 🎨 Templates HTML (INTERFAZ DE USUARIO COMPLETA)
Views y Templates Completos Disponibles
¡TODAS las 7 views + 6 templates HTML en un solo documento!
Incluye explicación completa de cómo se conectan MySQL y MongoDB
📚 Abrir Documento Completo✅ Código completo listo para copiar | ✅ Explicación detallada | ✅ Sistema híbrido MySQL + MongoDB
⏱️ FASE 4: Crear Interfaz de Usuario (30 minutos)
Objetivo: Crear páginas HTML para visualizar y gestionar libros en MongoDB
Archivos a crear: 5 templates HTML + 1 archivo base
Estructura de Templates
Crea esta estructura de carpetas:
biblioteca_project/
├── libros/
│ ├── templates/
│ │ └── libros/
│ │ ├── base.html # ← Template base con header/footer
│ │ ├── inicio.html # ← Página de inicio
│ │ ├── lista_libros.html # ← Catálogo de libros
│ │ ├── detalle_libro.html # ← Vista de un libro específico
│ │ ├── crear_libro.html # ← Formulario para agregar libro
│ │ └── estadisticas.html # ← Dashboard con gráficas
│ ├── static/
│ │ └── libros/
│ │ └── css/
│ │ └── estilos.css # ← Estilos personalizados
📄 Template 1: base.html (Template Base)
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Biblioteca UTH{% endblock %}</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
:root {
--primary-color: #f093fb;
--secondary-color: #f5576c;
--dark-color: #2c3e50;
--light-gray: #ecf0f1;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
min-height: 100vh;
padding: 20px;
}
.main-container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.navbar {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
}
.navbar-brand, .navbar-nav .nav-link {
color: white !important;
}
.navbar-nav .nav-link:hover {
background: rgba(255,255,255,0.2);
border-radius: 5px;
}
.content-wrapper {
padding: 40px;
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.2);
}
.btn-primary {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border: none;
}
.btn-primary:hover {
background: linear-gradient(135deg, #f5576c 0%, #f093fb 100%);
}
footer {
background: var(--dark-color);
color: white;
text-align: center;
padding: 30px;
margin-top: 50px;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<div class="main-container">
<!-- Navigation Bar -->
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'inicio' %}">
<i class="fas fa-book-open"></i> Biblioteca UTH
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'inicio' %}">
<i class="fas fa-home"></i> Inicio
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'lista_libros' %}">
<i class="fas fa-books"></i> Catálogo
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'crear_libro' %}">
<i class="fas fa-plus-circle"></i> Agregar Libro
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'estadisticas' %}">
<i class="fas fa-chart-bar"></i> Estadísticas
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Messages (Django Messages Framework) -->
{% if messages %}
<div class="container mt-3">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Main Content -->
<div class="content-wrapper">
{% block content %}{% endblock %}
</div>
<!-- Footer -->
<footer>
<p>© 2026 Biblioteca UTH - Proyecto MongoDB + Django</p>
<p><small>Desarrollado con ❤️ usando MongoDB Atlas, Django y Bootstrap</small></p>
</footer>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
📄 Template 2: inicio.html (Página de Inicio)
{% extends 'libros/base.html' %}
{% block title %}Inicio - Biblioteca UTH{% endblock %}
{% block content %}
<div class="text-center mb-5">
<h1 class="display-3"><i class="fas fa-database"></i> Bienvenido a Biblioteca UTH</h1>
<p class="lead">Sistema de gestión de libros con MongoDB Atlas y Django</p>
</div>
<div class="row mb-4">
<!-- Card: Total Libros -->
<div class="col-md-4 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-books fa-3x text-primary mb-3"></i>
<h3 class="card-title">{{ total_libros }}</h3>
<p class="card-text">Libros Registrados</p>
<a href="{% url 'lista_libros' %}" class="btn btn-primary">Ver Catálogo</a>
</div>
</div>
</div>
<!-- Card: Agregar Libro -->
<div class="col-md-4 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-plus-circle fa-3x text-success mb-3"></i>
<h3 class="card-title">Agregar</h3>
<p class="card-text">Nuevo Libro</p>
<a href="{% url 'crear_libro' %}" class="btn btn-success">Crear Libro</a>
</div>
</div>
</div>
<!-- Card: Estadísticas -->
<div class="col-md-4 mb-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-chart-bar fa-3x text-warning mb-3"></i>
<h3 class="card-title">Analytics</h3>
<p class="card-text">Ver Estadísticas</p>
<a href="{% url 'estadisticas' %}" class="btn btn-warning">Dashboard</a>
</div>
</div>
</div>
</div>
<div class="alert alert-info">
<h4><i class="fas fa-info-circle"></i> Información del Sistema</h4>
<ul>
<li><strong>Base de Datos:</strong> MongoDB Atlas (Cloud)</li>
<li><strong>Framework:</strong> Django {{ django_version }}</li>
<li><strong>Driver:</strong> PyMongo {{ pymongo_version }}</li>
</ul>
</div>
{% endblock %}
✅ CHECKPOINT 6: Templates base creados
Archivos creados hasta ahora:
- ✅
base.html- Template base con navbar y estilos - ✅
inicio.html- Página principal con dashboard
Siguiente: Crear templates para listar y gestionar libros
📝 NOTA: Views correspondientes
Para que estos templates funcionen, necesitas crear las views en libros/views.py
Ver sección completa de código de views más adelante en la guía
7. 🛡️ Mejores Prácticas y Seguridad
🔐 Seguridad en MongoDB Atlas
❌ NUNCA hagas esto:
- Hardcodear connection strings con credenciales en código
- Usar contraseña débil para usuario de BD (123456, admin, password)
- Permitir acceso desde "0.0.0.0/0" (cualquier IP) en producción
- Subir archivos .env con credenciales a GitHub público
- Compartir cluster entre proyectos sin segregación de datos
- Deshabilitar autenticación en desarrollo
✅ SÍ haz esto:
- Variables de Entorno:
.env (en raíz del proyecto)
MONGODB_URI=mongodb+srv://usuario:password@cluster0.mongodb.net/biblioteca?retryWrites=true&w=majority MONGODB_DB_NAME=biblioteca_dbsettings.py (Seguro)from decouple import config MONGODB_SETTINGS = { 'URI': config('MONGODB_URI'), # Lee de .env 'DB_NAME': config('MONGODB_DB_NAME', default='biblioteca_db') } # Verificar que esté configurado if not MONGODB_SETTINGS['URI']: raise ValueError("MONGODB_URI no configurada en .env").gitignore (CRÍTICO).env *.pyc __pycache__/ venv/ db.sqlite3 /staticfiles/ *.log - Contraseña Fuerte:
- Mínimo 16 caracteres
- Mayúsculas + minúsculas + números + símbolos
- Usar generador de contraseñas
- Ejemplo:
xK9$mP2@vL5#nQ8&wR4
- Whitelist de IPs en Atlas:
- Network Access → Add IP Address
- Desarrollo: Tu IP pública actual
- Producción: IP del servidor (AWS, Heroku, etc.)
- ⚠️ Evitar "Allow Access from Anywhere" (0.0.0.0/0)
- Roles y Permisos:
Database Access (MongoDB Atlas)
// Usuario solo lectura (reportes) Role: "read" Database: "biblioteca_db" // Usuario lectura-escritura (app) Role: "readWrite" Database: "biblioteca_db" // Administrador (DBA) Role: "dbAdmin" Database: "biblioteca_db"
🚀 Optimización de Rendimiento
1. Índices Estratégicos
# Índice simple (1 campo)
db.libros.create_index([("isbn", 1)]) # 1 = ascendente, -1 = descendente
# Índice compuesto (múltiples campos)
db.libros.create_index([
("categoria", 1),
("precio", -1)
])
# Índice de texto (búsqueda full-text)
db.libros.create_index([("titulo", "text"), ("descripcion", "text")])
# Índice único (sin duplicados)
db.libros.create_index([("isbn", 1)], unique=True)
# Ver índices existentes
db.libros.get_indexes()
2. Proyecciones (Solo Campos Necesarios)
# ❌ Malo: Traer todos los campos (documento completo)
libros = db.libros.find({})
# ✅ Bueno: Solo campos necesarios
libros = db.libros.find(
{}, # Filtro vacío = todos los documentos
{"titulo": 1, "precio": 1, "_id": 0} # 1 = incluir, 0 = excluir
)
# Resultado: [{"titulo": "...", "precio": 15.99}, ...]
3. Paginación con skip y limit
def obtener_libros_paginados(pagina=1, por_pagina=20):
skip = (pagina - 1) * por_pagina
libros = db.libros.find().skip(skip).limit(por_pagina)
total = db.libros.count_documents({})
return {
'libros': list(libros),
'pagina': pagina,
'total': total,
'paginas': (total + por_pagina - 1) // por_pagina
}
4. Connection Pooling
from pymongo import MongoClient
# ✅ Singleton: Una sola instancia de cliente
class MongoDBConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.client = MongoClient(
settings.MONGODB_URI,
maxPoolSize=50, # Max conexiones en pool
minPoolSize=10 # Min conexiones siempre abiertas
)
return cls._instance
def get_db(self):
return self.client[settings.MONGODB_DB_NAME]
📊 Modelado de Datos en MongoDB
Reglas de Oro:
- Embedding para relaciones 1-a-pocos:
- Libro → Reseñas (1 libro tiene 10-20 reseñas)
- Usuario → Direcciones (1 usuario tiene 2-3 direcciones)
- Referencing para relaciones muchos-a-muchos:
- Libros ↔ Categorías (1000+ libros en 1 categoría)
- Usuarios ↔ Libros Favoritos
- Límite de 16 MB por documento:
- No almacenar archivos grandes (usar GridFS o S3)
- Limitar arrays anidados (max 100-200 elementos)
- Diseñar para queries comunes:
- Si siempre consultas libro+autor juntos → embedding
- Si actualizas autor frecuentemente → referencing
8. 💡 Casos de Uso Avanzados
1. Agregaciones Complejas (Pipeline)
Objetivo: Obtener estadísticas de libros por categoría.
pipeline = [
{
"$unwind": "$categorias" # Descomponer array de categorías
},
{
"$group": { # Agrupar por categoría
"_id": "$categorias",
"total_libros": { "$sum": 1 },
"precio_promedio": { "$avg": "$precio" },
"precio_max": { "$max": "$precio" },
"precio_min": { "$min": "$precio" }
}
},
{
"$sort": { "total_libros": -1 } # Ordenar descendente
},
{
"$limit": 10 # Top 10 categorías
}
]
resultado = db.libros.aggregate(pipeline)
# Resultado:
# [
# {"_id": "Literatura", "total_libros": 150, "precio_promedio": 18.50, ...},
# {"_id": "Ficción", "total_libros": 120, "precio_promedio": 16.75, ...},
# ...
# ]
2. Búsqueda Full-Text
Objetivo: Buscar libros por palabras clave en título y descripción.
# Crear índice de texto
db.libros.create_index([
("titulo", "text"),
("descripcion", "text")
])
# Buscar libros que contengan "amor" o "soledad"
resultados = db.libros.find({
"$text": {
"$search": "amor soledad"
}
})
# Buscar frase exacta
resultados = db.libros.find({
"$text": {
"$search": "\"Cien Años de Soledad\""
}
})
# Búsqueda con score (relevancia)
resultados = db.libros.find(
{ "$text": { "$search": "amor" } },
{ "score": { "$meta": "textScore" } }
).sort([("score", {"$meta": "textScore"})])
3. Transacciones Multi-Documento (MongoDB 4.0+)
Objetivo: Asegurar consistencia en operaciones que afectan múltiples colecciones.
from pymongo import MongoClient
client = MongoClient(MONGODB_URI)
session = client.start_session()
try:
with session.start_transaction():
# Reducir stock del libro
db.libros.update_one(
{"_id": libro_id},
{"$inc": {"stock": -1}},
session=session
)
# Crear orden de compra
db.ordenes.insert_one({
"libro_id": libro_id,
"usuario_id": usuario_id,
"fecha": datetime.now()
}, session=session)
# Si todo OK, commit
session.commit_transaction()
print("✅ Compra exitosa")
except Exception as e:
# Si hay error, rollback
session.abort_transaction()
print(f"❌ Error: {e}")
finally:
session.end_session()
🆘 SOLUCIÓN DE PROBLEMAS (TROUBLESHOOTING)
📋 Guía Rápida de Diagnóstico
Si algo no funciona, sigue este orden:
- 🔍 Lee el mensaje de error COMPLETO
- 📝 Identifica el tipo de error (conexión, autenticación, sintaxis, etc.)
- 🔧 Busca la solución específica en las secciones de abajo
- ✅ Verifica el fix ejecutando el código de nuevo
- ❓ Si persiste, revisa logs y contacta soporte
🔴 Problema 1: Error de Conexión a MongoDB Atlas
❌ Síntomas:
pymongo.errors.ServerSelectionTimeoutError:
localhost:27017: [Errno 111] Connection refused,
Timeout: 30s, Topology Description:
<TopologyDescription id: ... >
O también:
pymongo.errors.ConfigurationError:
The "dnspython" module must be installed to use mongodb+srv:// URIs
O también:
pymongo.errors.OperationFailure:
bad auth : authentication failed
✅ Soluciones (prueba en orden):
Solución 1: Verificar Connection String
- 📋 Abre tu archivo
mongodb_credentials.txt - 🔍 Verifica que NO tenga los símbolos
<password> - 🔑 Asegúrate de que la contraseña esté CORRECTA (sin espacios extra)
- 🌐 Verifica que diga
mongodb+srv://(NOmongodb://)
# BIEN ✅ (sin < > y con contraseña real):
mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.abc123.mongodb.net/biblioteca_logs?retryWrites=true&w=majority
# MAL ❌ (con < > literal):
mongodb+srv://biblioteca_admin:<password>@bibliotecacluster.abc123.mongodb.net/
Solución 2: Instalar dnspython
pip install dnspython==2.4.2
# O versión específica compatible:
pip install "pymongo[srv]"
Solución 3: Verificar IP Whitelist en Atlas
- 🌐 Ve a cloud.mongodb.com
- 🔍 Abre tu proyecto → "Network Access" (menú izquierdo)
- 👁️ Verifica que tu IP esté en la lista o que esté
0.0.0.0/0 - 🟢 Si NO está, haz clic en "Add IP Address"
- ✅ Agrega
0.0.0.0/0(permite todas las IPs - solo para desarrollo) - ⏳ Espera 1-2 minutos a que se aplique el cambio
Solución 4: Verificar credenciales de usuario
- 🔐 Ve a Atlas → Proyecto → "Database Access"
- 👤 Verifica que el usuario
biblioteca_adminexista - 🔑 Si no recuerdas la contraseña: Haz clic en "Edit" → "Edit Password"
- 🆕 Genera nueva contraseña y actualiza tu connection string
Solución 5: Probar conexión manualmente
from pymongo import MongoClient
import sys
# Reemplaza con tu connection string real:
MONGO_URI = "mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.abc123.mongodb.net/biblioteca_logs?retryWrites=true&w=majority"
try:
client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
client.server_info() # Forzar conexión
print("✅ Conexión EXITOSA a MongoDB Atlas!")
print(f"📊 Versión MongoDB: {client.server_info()['version']}")
print(f"🗄️ Bases de datos disponibles: {client.list_database_names()}")
except Exception as e:
print(f"❌ Error de conexión: {e}")
sys.exit(1)
finally:
client.close()
🔴 Problema 2: Error "No module named 'pymongo'" o "No module named 'djongo'"
❌ Síntomas:
ModuleNotFoundError: No module named 'pymongo'
ModuleNotFoundError: No module named 'djongo'
ModuleNotFoundError: No module named 'dnspython'
✅ Soluciones:
Solución 1: Verificar que el entorno virtual esté ACTIVADO
# Verifica si el prompt tiene (venv) al inicio:
# (venv) PS C:\proyecto> ← ✅ ACTIVADO
# PS C:\proyecto> ← ❌ NO ACTIVADO
# Si NO está activado:
.\venv\Scripts\Activate
# Ahora deberías ver:
# (venv) PS C:\proyecto>
Solución 2: Instalar todas las dependencias de nuevo
# Asegúrate de estar en el entorno virtual:
.\venv\Scripts\Activate
# Instala TODO de nuevo:
pip install pymongo==4.6.0
pip install djongo==1.3.6
pip install dnspython==2.4.2
pip install "pymongo[srv]"
# Verifica instalación:
pip list | Select-String "pymongo|djongo|dnspython"
# Deberías ver:
# djongo 1.3.6
# dnspython 2.4.2
# pymongo 4.6.0
Solución 3: Usar pip3 si pip no funciona
pip3 install pymongo djongo dnspython
🔴 Problema 3: Error "Authentication failed" o "bad auth"
❌ Síntomas:
pymongo.errors.OperationFailure: bad auth : authentication failed
pymongo.errors.OperationFailure: Authentication failed.
✅ Soluciones:
Solución 1: Verificar usuario y contraseña
- 🔍 Verifica que el usuario en el connection string coincida con el de Atlas
- 🔑 Verifica que la contraseña NO tenga caracteres especiales problemáticos
- 🌐 Ve a Atlas → "Database Access" → Verifica el usuario
# Si tu contraseña tiene estos caracteres, puede fallar:
# @ : / ? # [ ] % &
# Ejemplo de contraseña PROBLEMÁTICA:
# MiPass@123# ← El @ puede confundirse con el separador del host
# Solución 1: Cambiar contraseña a una sin caracteres especiales
# Ejemplo: MiPassword2026
# Solución 2: URL-encode los caracteres especiales
# @ → %40
# # → %23
# : → %3A
Solución 2: Resetear contraseña en Atlas
- 🌐 Ve a cloud.mongodb.com
- 🔐 Proyecto → "Database Access"
- ✏️ Busca tu usuario → "Edit"
- 🔑 "Edit Password" → Genera nueva contraseña SIMPLE (sin símbolos)
- 💾 Guarda y actualiza tu connection string
🔴 Problema 4: Django no encuentra modelos de MongoDB (djongo)
❌ Síntomas:
django.db.utils.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
O también:
RuntimeError: Model class ... doesn't declare an explicit app_label
✅ Soluciones:
Solución 1: Configurar Meta.managed = False en modelos MongoDB
from djongo import models
class LogActividad(models.Model):
usuario = models.CharField(max_length=100)
accion = models.CharField(max_length=50)
class Meta:
db_table = 'logs_actividad'
managed = False # ← IMPORTANTE: Django no intentará migraciones
app_label = 'libros' # ← Especifica la app explícitamente
Solución 2: Usar database router correctamente
class MongoDBRouter:
"""
Router para dirigir operaciones de modelos específicos a MongoDB
"""
mongodb_models = ['LogActividad', 'EstadisticaLibro'] # Modelos que usan MongoDB
def db_for_read(self, model, **hints):
if model.__name__ in self.mongodb_models:
return 'mongodb'
return 'default'
def db_for_write(self, model, **hints):
if model.__name__ in self.mongodb_models:
return 'mongodb'
return 'default'
def allow_migrate(self, db, app_label, model_name=None, **hints):
if model_name in [m.lower() for m in self.mongodb_models]:
return db == 'mongodb'
return db == 'default'
Solución 3: Migrar solo MySQL, no MongoDB
# Migrar SOLO la base de datos MySQL (default):
python manage.py makemigrations
python manage.py migrate --database=default
# NO ejecutes migrate para MongoDB (se maneja directo con PyMongo)
# MongoDB NO necesita migraciones (NoSQL es schema-less)
🔴 Problema 5: "BSON library not found" o errores de serialización
❌ Síntomas:
ImportError: cannot import name 'BSON' from 'bson'
TypeError: Object of type ObjectId is not JSON serializable
✅ Soluciones:
Solución 1: Convertir ObjectId a string en serialización
from bson import ObjectId
import json
def listar_libros(request):
libros = list(db.libros.find())
# Convertir ObjectId a string para JSON:
for libro in libros:
libro['_id'] = str(libro['_id']) # ← Conversión importante
return JsonResponse({'libros': libros}, safe=False)
Solución 2: Usar JSONEncoder personalizado
from bson import ObjectId
from datetime import datetime
import json
class MongoJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, ObjectId):
return str(obj)
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
# Uso en views:
from django.http import JsonResponse
import json
def listar_libros(request):
libros = list(db.libros.find())
libros_json = json.dumps(libros, cls=MongoJSONEncoder)
return JsonResponse(json.loads(libros_json), safe=False)
🔴 Problema 6: Cluster de Atlas en estado "Paused" o "Stopped"
❌ Síntomas:
- El cluster aparece con estado "Paused" en Atlas dashboard
- Error: "Server selection timeout" después de 30 segundos
- No se puede conectar aunque las credenciales sean correctas
✅ Solución:
- 🌐 Ve a cloud.mongodb.com
- 🔍 Localiza tu cluster en el dashboard
- ▶️ Si dice "Paused": Haz clic en los 3 puntitos → "Resume"
- ⏳ Espera 2-3 minutos a que el cluster se active
- ✅ El estado debe cambiar a "Active" (bolita verde)
- 🔄 Intenta conectar nuevamente
ℹ️ ¿Por qué se pausa?
Los clusters gratuitos M0 se pausan automáticamente después de 60 días de inactividad para ahorrar recursos.
Solución permanente: Usa el cluster regularmente (al menos una vez por semana).
📞 ¿Aún tienes problemas?
🛠️ Pasos adicionales de debugging:
- Revisa logs completos de Django:
PowerShell
python manage.py runserver --noreload - Verifica versiones instaladas:
PowerShell
pip list | Select-String "django|pymongo|djongo|dnspython" - Consulta documentación oficial:
- Contacta a tu instructor: Proporciona screenshot del error y los pasos que ya intentaste
✅ Checklist Final de Verificación:
| Item | ¿Cómo verificar? | Estado |
|---|---|---|
| Connection String correcto | Sin <password>, con contraseña real |
☐ |
| Usuario existe en Atlas | Database Access → Ver lista de usuarios | ☐ |
| IP en whitelist | Network Access → Ver IPs permitidas | ☐ |
| Cluster activo | Dashboard → Estado "Active" (verde) | ☐ |
| PyMongo instalado | pip list | Select-String pymongo |
☐ |
| dnspython instalado | pip list | Select-String dnspython |
☐ |
| Entorno virtual activado | Prompt tiene (venv) al inicio |
☐ |
9. 📚 Recursos Adicionales y Referencias
📖 Documentación Oficial
- MongoDB Manual: docs.mongodb.com/manual/
- MongoDB Atlas Documentation: docs.atlas.mongodb.com/
- PyMongo Documentation: pymongo.readthedocs.io/
- MongoDB University (Cursos Gratis): university.mongodb.com/
- Django Documentation: docs.djangoproject.com/
🎓 Cursos Recomendados (Gratuitos)
- M001: MongoDB Basics (MongoDB University)
- M121: The MongoDB Aggregation Framework (MongoDB University)
- M220P: MongoDB for Python Developers (MongoDB University)
- freeCodeCamp: MongoDB Crash Course (YouTube)
🛠️ Herramientas Útiles
| Herramienta | Descripción | Uso |
|---|---|---|
| MongoDB Compass | GUI oficial de MongoDB | Explorar datos visualmente, probar queries |
| Studio 3T | IDE avanzado para MongoDB | Queries complejas, SQL to MongoDB, import/export |
| Robo 3T (Robomongo) | Cliente ligero de MongoDB | Desarrollo rápido, shell embebido |
| mongodump/mongorestore | Herramientas CLI de backup | Backups y restauración de datos |
| Postman | Cliente de APIs REST | Probar endpoints de tu API Django |
💼 Proyectos de Ejemplo para Inspiración
- Sistema de Gestión de Contenidos (CMS)
- Artículos con metadatos dinámicos
- Categorización flexible
- Búsqueda full-text
- E-commerce con Catálogo Flexible
- Productos con atributos variables
- Reseñas de usuarios
- Recomendaciones basadas en historial
- Sistema de Logs y Analytics
- Almacenar millones de eventos
- Agregaciones para estadísticas
- Dashboards en tiempo real
- Red Social o Foro
- Posts con comentarios anidados
- Likes, shares, mentions
- Timeline personalizado
7. 📖 Glosario de Términos
Términos de MongoDB y NoSQL
Base de datos "Not Only SQL" - diseñadas para datos no estructurados o semi-estructurados con esquemas flexibles.
Unidad básica de datos en MongoDB, equivalente a una fila en SQL. Se almacena en formato BSON (Binary JSON).
Grupo de documentos en MongoDB, equivalente a una tabla en SQL. No requiere esquema predefinido.
Binary JSON - formato binario que extiende JSON con tipos adicionales como Date, ObjectId, Binary data, etc.
Identificador único de 12 bytes generado automáticamente por MongoDB para cada documento (_id).
Técnica de almacenar documentos relacionados dentro de un documento principal (documentos anidados).
Técnica de almacenar referencias a documentos en otras colecciones, similar a foreign keys en SQL.
Particionamiento horizontal de datos entre múltiples servidores para mejorar escalabilidad.
Grupo de instancias MongoDB que mantienen el mismo conjunto de datos para alta disponibilidad.
Framework para procesamiento de datos que permite transformar y analizar documentos en múltiples etapas.
Estructura de datos que mejora la velocidad de las consultas, similar a índices en SQL.
Biblioteca Python que permite usar MongoDB como backend de Django manteniendo el ORM de Django.
📦 LO QUE DEBE CONTENER EL PROYECTO
📋 Checklist del Proyecto
Asegúrate de cumplir con TODOS estos requisitos antes de entregar:
✅ 1. Código Fuente Completo
Estructura de carpetas requerida:
biblioteca_project/ # ← Raíz del proyecto
├── manage.py
├── test_mongo.py # ✅ Script de prueba de conexión
├── init_mongodb.py # ✅ Script de inicialización de MongoDB
├── requirements.txt # ✅ Dependencias del proyecto
├── .gitignore # ✅ Archivos a ignorar en Git
├── db.sqlite3 # Base de datos MySQL local
│
├── biblioteca_project/ # ← Carpeta de configuración
│ ├── __init__.py
│ ├── settings.py # ✅ Configuración MySQL + MongoDB Atlas
│ ├── urls.py # ✅ URLs principales del proyecto
│ ├── wsgi.py
│ └── asgi.py
│
├── biblioteca/ # ← Aplicación Django
│ ├── __init__.py
│ ├── models.py # ✅ Modelos MySQL (Prestamo, Multa)
│ ├── views.py # ✅ 7 vistas híbridas (MySQL + MongoDB)
│ ├── urls.py # ✅ Rutas de la aplicación
│ ├── admin.py # ✅ Configuración del panel admin
│ ├── apps.py
│ ├── tests.py
│ │
│ ├── migrations/ # ← Migraciones de MySQL
│ │ └── __init__.py
│ │
│ └── templates/ # ← Carpeta de templates HTML
│ └── biblioteca/ # ← Subcarpeta con nombre de app
│ ├── base.html # ✅ Template base (Bootstrap 5)
│ ├── inicio.html # ✅ Página de inicio
│ ├── dashboard.html # ✅ Dashboard con gráficas
│ ├── listar_libros.html # ✅ Catálogo completo (MongoDB)
│ ├── lista_libros.html # ✅ Vista alternativa de libros
│ ├── detalle_libro.html # ✅ Detalle de libro individual
│ └── mis_prestamos.html # ✅ Vista híbrida (MySQL+MongoDB) ⭐
│
└── venv/ # ← Entorno virtual de Python
## 🎯 Archivos Principales del Sistema:
### 1. Configuración (biblioteca_project/)
- settings.py → Conexiones MySQL + MongoDB Atlas
- urls.py → Incluye path('', include('biblioteca.urls'))
### 2. Aplicación (biblioteca/)
- models.py → Prestamo, Multa (MySQL con libro_id)
- views.py → 7 vistas que usan ambas bases de datos
- urls.py → Todas las rutas (dashboard, libros, prestamos)
- admin.py → Panel de administración
### 3. Templates (biblioteca/templates/biblioteca/)
- base.html → Plantilla base con Bootstrap 5 + navbar
- dashboard.html → Estadísticas híbridas con Chart.js
- mis_prestamos.html → Consulta híbrida usando libro_id
### 4. Scripts de Utilidad
- init_mongodb.py → Crea 4 bases de datos en Atlas
- test_mongo.py → Verifica conexión a MongoDB Atlas
## 🔗 Campo Común que Conecta Ambas BDs:
libro_id → Enlaza registros entre MySQL y MongoDB Atlas