📚

Views y Templates Completos

MongoDB + Django - Sistema Híbrido

✅ Documento Completo

Este documento contiene:

  • MODELO Libro completo (models.py)
  • TODAS las views completas (7 funciones)
  • TODOS los templates HTML (7 archivos)
  • Configuración admin.py
  • Explicación de conexión MySQL + MongoDB
  • URLconf completo

📂 Estructura de Carpetas del Proyecto

Todos los archivos de este documento deben ir en estas ubicaciones:

biblioteca_project/                      ← Raíz del proyecto
├── biblioteca_project/                  ← Carpeta de configuración
│   ├── settings.py                      ← Configuración de MongoDB + MySQL
│   └── urls.py                          ← URLs principales del proyecto
│
└── biblioteca/                          ← Aplicación Django
    ├── models.py                        ← ✅ Modelos (Prestamo, Multa)
    ├── views.py                         ← ✅ Vistas híbridas (MySQL + MongoDB)
    ├── urls.py                          ← ✅ URLs de la app
    ├── admin.py                         ← ✅ Configuración del admin
    │
    └── templates/                       ← Carpeta de templates
        └── biblioteca/                  ← Subcarpeta con nombre de la app
            ├── base.html                ← ✅ Template base
            ├── dashboard.html           ← ✅ Dashboard híbrido
            ├── listar_libros.html       ← ✅ Catálogo MongoDB
            ├── mis_prestamos.html       ← ✅ Vista híbrida (MySQL+MongoDB)
            ├── inicio.html              ← Template adicional
            ├── lista_libros.html        ← Template adicional
            └── detalle_libro.html       ← Template adicional

💡 Nota: Cada sección de este documento muestra la ruta completa en un cuadro destacado.

🔗 Cómo se Conectan MySQL y MongoDB

💡 Concepto de Sistema Híbrido

Un sistema híbrido usa dos bases de datos simultáneamente para aprovechar lo mejor de cada una:

  • MySQL (SQL): Datos estructurados, relaciones, integridad
  • MongoDB (NoSQL): Datos flexibles, logs, métricas, escalabilidad

📊 Arquitectura del Sistema Híbrido

Diagrama de Conexión:

┌─────────────────────────────────────────────────────────────┐
│                      DJANGO APPLICATION                      │
│                                                              │
│   ┌──────────────────────────────────────────────────────┐ │
│   │                   settings.py                        │ │
│   │  ┌────────────────┐      ┌────────────────────────┐ │ │
│   │  │  DATABASES     │      │  MONGODB_CONNECTION   │ │ │
│   │  │  (MySQL)       │      │  (MongoDB Atlas)      │ │ │
│   │  └────────┬───────┘      └────────┬───────────────┘ │ │
│   └───────────┼──────────────────────┼──────────────────┘ │
│               │                      │                     │
│      ┌────────▼────────┐    ┌────────▼────────┐          │
│      │   models.py     │    │  mongo_utils.py │          │
│      │  (Django ORM)   │    │   (PyMongo)     │          │
│      └────────┬────────┘    └────────┬────────┘          │
│               │                      │                     │
│      ┌────────▼──────────────────────▼────────┐          │
│      │           views.py                      │          │
│      │  - Usa ambas bases de datos            │          │
│      │  - MySQL: Libros, Usuarios             │          │
│      │  - MongoDB: Logs, Estadísticas         │          │
│      └────────┬────────────────────────────────┘          │
└───────────────┼──────────────────────────────────────────┘
                │
    ┌───────────▼───────────┐
    │     templates/        │
    │  - Muestran datos     │
    │    de ambas BD        │
    └───────────────────────┘

                ↓                                ↓
        
    ┌─────────────────┐              ┌──────────────────────┐
    │   MySQL         │              │   MongoDB Atlas      │
    │                 │              │                      │
    │  ┌───────────┐  │              │  ┌────────────────┐ │
    │  │  Libros   │  │              │  │  biblioteca    │ │
    │  │  (table)  │  │              │  │  _logs (DB)    │ │
    │  ├───────────┤  │              │  │                │ │
    │  │ id        │  │              │  │ Collections:   │ │
    │  │ titulo    │  │              │  │ - libros       │ │
    │  │ autor     │  │              │  │ - prestamos    │ │
    │  │ isbn      │  │              │  │ - estadisticas │ │
    │  │ precio    │  │              │  └────────────────┘ │
    │  └───────────┘  │              └──────────────────────┘
    │                 │
    │  ┌───────────┐  │
    │  │ Usuarios  │  │
    │  │ (table)   │  │
    │  └───────────┘  │
    └─────────────────┘

FLUJO DE DATOS:

1. Usuario accede a http://localhost:8000/libros/lista/
2. Django ejecuta vista lista_libros()
3. Vista consulta:
   - MySQL: Libros guardados (Django ORM)
   - MongoDB: Logs de acceso (PyMongo)
4. Registra en MongoDB: "Usuario X consultó lista de libros"
5. Renderiza template con datos de ambas BD
6. Usuario ve lista de libros + estadísticas combinadas

🔄 ¿Qué Comparten en Común?

Aspecto MySQL MongoDB Conexión
Identificador del Libro libro.id (int) libro_id (string) Se pasa como referencia
Datos Básicos Título, Autor, ISBN Log de quién lo consultó MySQL guarda el libro, Mongo registra accesos
Estadísticas Cantidad en stock Views, préstamos, popularidad Se suman para mostrar métricas completas
Usuario Datos del usuario (tabla) Actividad del usuario (logs) user_id vincula ambas BD

🎯 Ejemplo Práctico de Conexión:

Escenario: Usuario consulta el libro "El Principito"

PASO 1 - Django consulta MySQL:
libro = Libro.objects.get(id=5)  # Obtiene: título, autor, precio
# Resultado: {'id': 5, 'titulo': 'El Principito', 'autor': 'Saint-Exupéry'}

PASO 2 - Django consulta MongoDB (estadísticas):
stats = db.estadisticas.find_one({'libro_id': '5'})
# Resultado: {'libro_id': '5', 'views': 1250, 'prestamos': 87}

PASO 3 - Django registra la consulta en MongoDB:
db.logs.insert_one({
    'libro_id': '5',
    'titulo': libro.titulo,     # ← Dato de MySQL
    'user_id': request.user.id, # ← Dato de MySQL (Django auth)
    'accion': 'consulta',
    'timestamp': datetime.now()
})

PASO 4 - Template muestra datos combinados:
Título: El Principito (MySQL)
Autor: Saint-Exupéry (MySQL)
Visto: 1250 veces (MongoDB)
Prestado: 87 veces (MongoDB)

📄 ARCHIVO COMPLETO: biblioteca/models.py

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── models.py  ← ESTE ARCHIVO

🏗️ Modelo Libro en Django (MySQL):

Define la estructura de datos que se guardará en MySQL usando Django ORM.

# ========================================
# ARCHIVO: libros/models.py
# Modelo de Libro para MySQL usando Django ORM
# ========================================

from django.db import models
from django.utils import timezone

class Libro(models.Model):
    """
    Modelo principal para almacenar libros en MySQL.
    Este modelo define la estructura relacional de los datos.
    """
    # Campos principales
    titulo = models.CharField(
        max_length=200, 
        verbose_name="Título del Libro",
        help_text="Título completo del libro (máximo 200 caracteres)"
    )
    
    autor = models.CharField(
        max_length=100, 
        verbose_name="Autor",
        help_text="Nombre del autor o autores"
    )
    
    isbn = models.CharField(
        max_length=17, 
        unique=True, 
        verbose_name="ISBN",
        help_text="Código ISBN único (formato: 978-3-16-148410-0)"
    )
    
    fecha_publicacion = models.DateField(
        verbose_name="Fecha de Publicación",
        help_text="Fecha original de publicación del libro"
    )
    
    precio = models.DecimalField(
        max_digits=10, 
        decimal_places=2,
        verbose_name="Precio",
        help_text="Precio del libro (formato: 000000.00)"
    )
    
    stock = models.PositiveIntegerField(
        default=0,
        verbose_name="Stock Disponible",
        help_text="Cantidad de ejemplares en inventario"
    )
    
    descripcion = models.TextField(
        blank=True, 
        null=True,
        verbose_name="Descripción",
        help_text="Descripción opcional del contenido del libro"
    )
    
    # Campos de auditoría (automáticos)
    fecha_creacion = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Fecha de Creación"
    )
    
    fecha_actualizacion = models.DateTimeField(
        auto_now=True,
        verbose_name="Última Actualización"
    )
    
    activo = models.BooleanField(
        default=True,
        verbose_name="Activo",
        help_text="Si está marcado, el libro está disponible en el sistema"
    )

    class Meta:
        verbose_name = "Libro"
        verbose_name_plural = "Libros"
        ordering = ['-fecha_creacion']  # Ordenar por fecha de creación descendente
        indexes = [
            models.Index(fields=['isbn']),  # Índice para búsquedas por ISBN
            models.Index(fields=['autor']),  # Índice para búsquedas por autor
            models.Index(fields=['titulo']),  # Índice para búsquedas por título
        ]

    def __str__(self):
        """Representación en string del objeto libro"""
        return f"{self.titulo} - {self.autor}"
    
    def get_absolute_url(self):
        """URL para ver los detalles del libro"""
        from django.urls import reverse
        return reverse('detalle_libro', args=[str(self.id)])
    
    @property
    def disponible(self):
        """Verifica si el libro está disponible (stock > 0 y activo)"""
        return self.activo and self.stock > 0
    
    @property 
    def precio_formateado(self):
        """Retorna el precio formateado con símbolo de moneda"""
        return f"${self.precio:,.2f}"
    
    def reducir_stock(self, cantidad=1):
        """Reduce el stock del libro (útil para préstamos)"""
        if self.stock >= cantidad:
            self.stock -= cantidad
            self.save()
            return True
        return False
    
    def aumentar_stock(self, cantidad=1):
        """Aumenta el stock del libro (útil para devoluciones)"""
        self.stock += cantidad
        self.save()
    
    def save(self, *args, **kwargs):
        """Sobrescribe el método save para validaciones adicionales"""
        # Convertir título a title case
        self.titulo = self.titulo.title()
        
        # Validar ISBN básico (solo números y guiones)
        import re
        if not re.match(r'^[\d-]+$', self.isbn):
            raise ValueError("El ISBN solo puede contener números y guiones")
        
        super().save(*args, **kwargs)

📄 ARCHIVO COMPLETO: biblioteca/admin.py

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── admin.py  ← ESTE ARCHIVO

⚙️ Configuración del Admin de Django:

Permite gestionar los libros desde el panel de administración.

# ========================================
# ARCHIVO: libros/admin.py
# Configuración del panel de administración para libros
# ========================================

from django.contrib import admin
from .models import Libro

@admin.register(Libro)
class LibroAdmin(admin.ModelAdmin):
    """
    Configuración avanzada del admin para el modelo Libro.
    Proporciona una interfaz rica para gestionar libros.
    """
    
    # Campos a mostrar en la lista
    list_display = [
        'titulo', 
        'autor', 
        'isbn', 
        'precio_formateado', 
        'stock', 
        'disponible',
        'fecha_publicacion',
        'activo'
    ]
    
    # Campos por los que se puede filtrar
    list_filter = [
        'activo',
        'fecha_publicacion', 
        'fecha_creacion',
        ('precio', admin.RangeNumericFilter),  # Filtro por rango de precio
        ('stock', admin.RangeNumericFilter),   # Filtro por rango de stock
    ]
    
    # Campos de búsqueda
    search_fields = [
        'titulo', 
        'autor', 
        'isbn',
        'descripcion'
    ]
    
    # Campos editables directamente en la lista
    list_editable = [
        'precio', 
        'stock', 
        'activo'
    ]
    
    # Ordenamiento por defecto
    ordering = ['-fecha_creacion']
    
    # Campos de solo lectura
    readonly_fields = [
        'fecha_creacion', 
        'fecha_actualizacion'
    ]
    
    # Organización de campos en el formulario
    fieldsets = [
        ('Información Básica', {
            'fields': ('titulo', 'autor', 'isbn')
        }),
        ('Detalles de Publicación', {
            'fields': ('fecha_publicacion', 'descripcion')
        }),
        ('Inventario y Precio', {
            'fields': ('precio', 'stock', 'activo'),
            'classes': ['collapse']  # Sección colapsable
        }),
        ('Auditoría', {
            'fields': ('fecha_creacion', 'fecha_actualizacion'),
            'classes': ['collapse']
        })
    ]
    
    # Filtro de fecha con jerarquía
    date_hierarchy = 'fecha_creacion'
    
    # Paginación
    list_per_page = 25
    list_max_show_all = 100
    
    # Acciones personalizadas
    actions = ['marcar_como_agotado', 'marcar_como_disponible', 'duplicar_libros']
    
    def marcar_como_agotado(self, request, queryset):
        """Acción personalizada: marcar libros como agotados"""
        updated = queryset.update(stock=0)
        self.message_user(
            request, 
            f'{updated} libros marcados como agotados.'
        )
    marcar_como_agotado.short_description = "Marcar como agotado (stock = 0)"
    
    def marcar_como_disponible(self, request, queryset):
        """Acción personalizada: activar libros"""
        updated = queryset.update(activo=True)
        self.message_user(
            request, 
            f'{updated} libros marcados como disponibles.'
        )
    marcar_como_disponible.short_description = "Marcar como disponibles"
    
    def duplicar_libros(self, request, queryset):
        """Acción personalizada: crear copias de libros seleccionados"""
        duplicados = 0
        for libro in queryset:
            # Crear copia con ISBN modificado
            nuevo_isbn = f"{libro.isbn}-COPY"
            Libro.objects.create(
                titulo=f"{libro.titulo} (Copia)",
                autor=libro.autor,
                isbn=nuevo_isbn,
                fecha_publicacion=libro.fecha_publicacion,
                precio=libro.precio,
                stock=libro.stock,
                descripcion=libro.descripcion,
                activo=libro.activo
            )
            duplicados += 1
        
        self.message_user(
            request, 
            f'{duplicados} libros duplicados exitosamente.'
        )
    duplicar_libros.short_description = "Crear copias de libros seleccionados"
    
    def get_queryset(self, request):
        """Optimizar consultas con select_related si fuera necesario"""
        return super().get_queryset(request)
    
    def save_model(self, request, obj, form, change):
        """Personalizar el guardado desde el admin"""
        super().save_model(request, obj, form, change)
        
        # Aquí se podría agregar lógica para sincronizar con MongoDB
        # Por ejemplo:
        # sync_libro_to_mongodb(obj)


# Personalización del título del admin
admin.site.site_header = "Sistema de Biblioteca UTH"
admin.site.site_title = "Biblioteca UTH Admin"
admin.site.index_title = "Panel de Administración"

📄 ARCHIVO COMPLETO: biblioteca/views.py

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── views.py  ← ESTE ARCHIVO

✅ Este archivo contiene TODAS las 7 vistas necesarias:

  1. inicio() - Página principal
  2. lista_libros() - Catálogo completo
  3. detalle_libro() - Vista individual
  4. crear_libro() - Agregar nuevo libro
  5. editar_libro() - Modificar libro existente
  6. eliminar_libro() - Borrar libro
  7. estadisticas() - Dashboard con gráficas
# ========================================
# ARCHIVO: libros/views.py
# Sistema Híbrido: MySQL (Django ORM) + MongoDB (PyMongo)
# ========================================

from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse
from django.contrib import messages
from .models import Libro  # ← Modelo en MySQL
from .mongo_utils import get_mongo_db  # ← Conexión a MongoDB
from datetime import datetime
from bson import ObjectId
import json

# Obtener conexión a MongoDB
db = get_mongo_db()


# ==========================================
# VISTA 1: Página de Inicio
# ==========================================
def inicio(request):
    """
    Vista principal del sistema.
    Muestra estadísticas generales combinando datos de MySQL y MongoDB.
    """
    # Contar libros en MySQL
    total_libros_mysql = Libro.objects.count()
    
    # Contar documentos de libros en MongoDB
    total_libros_mongo = db.libros.count_documents({})
    
    # Obtener top 5 libros más vistos desde MongoDB
    top_libros = list(db.estadisticas.find().sort('views', -1).limit(5))
    
    # Registrar visita a la página en MongoDB
    db.logs.insert_one({
        'pagina': 'inicio',
        'timestamp': datetime.now(),
        'ip': request.META.get('REMOTE_ADDR', 'unknown')
    })
    
    context = {
        'total_libros_mysql': total_libros_mysql,
        'total_libros_mongo': total_libros_mongo,
        'top_libros': top_libros
    }
    
    return render(request, 'libros/inicio.html', context)


# ==========================================
# VISTA 2: Lista de Libros (CRUD - Read All)
# ==========================================
def lista_libros(request):
    """
    Muestra catálogo completo de libros.
    Datos de MySQL (libro) + Estadísticas de MongoDB (views, préstamos).
    """
    # Obtener todos los libros de MySQL
    libros = Libro.objects.all().order_by('-fecha_publicacion')
    
    # Para cada libro, obtener estadísticas de MongoDB
    libros_con_stats = []
    for libro in libros:
        # Buscar estadísticas en MongoDB por libro_id
        stats = db.estadisticas.find_one({'libro_id': str(libro.id)})
        
        libro_data = {
            'libro': libro,  # Objeto del modelo MySQL
            'views': stats['views'] if stats else 0,
            'prestamos': stats['prestamos'] if stats else 0,
            'calificacion': stats.get('calificacion_promedio', 0.0) if stats else 0.0
        }
        libros_con_stats.append(libro_data)
    
    # Registrar acceso en MongoDB
    db.logs.insert_one({
        'accion': 'lista_libros',
        'timestamp': datetime.now(),
        'total_mostrados': len(libros_con_stats)
    })
    
    context = {'libros_con_stats': libros_con_stats}
    return render(request, 'libros/lista_libros.html', context)


# ==========================================
# VISTA 3: Detalle de un Libro (CRUD - Read One)
# ==========================================
def detalle_libro(request, libro_id):
    """
    Muestra detalles completos de un libro específico.
    Incrementa contador de views en MongoDB.
    """
    # Obtener libro de MySQL
    libro = get_object_or_404(Libro, id=libro_id)
    
    # Obtener/crear estadísticas en MongoDB
    stats = db.estadisticas.find_one({'libro_id': str(libro_id)})
    
    if stats:
        # Incrementar contador de views
        db.estadisticas.update_one(
            {'libro_id': str(libro_id)},
            {'$inc': {'views': 1}}  # $inc incrementa en 1
        )
        views = stats['views'] + 1
        prestamos = stats.get('prestamos', 0)
    else:
        # Crear documento de estadísticas si no existe
        db.estadisticas.insert_one({
            'libro_id': str(libro_id),
            'titulo': libro.titulo,
            'views': 1,
            'prestamos': 0,
            'calificacion_promedio': 0.0,
            'created_at': datetime.now()
        })
        views = 1
        prestamos = 0
    
    # Registrar acceso en logs de MongoDB
    db.logs.insert_one({
        'accion': 'ver_detalle',
        'libro_id': str(libro_id),
        'libro_titulo': libro.titulo,
        'timestamp': datetime.now(),
        'ip': request.META.get('REMOTE_ADDR', 'unknown')
    })
    
    context = {
        'libro': libro,
        'views': views,
        'prestamos': prestamos
    }
    
    return render(request, 'libros/detalle_libro.html', context)


# ==========================================
# VISTA 4: Crear Libro (CRUD - Create)
# ==========================================
def crear_libro(request):
    """
    Formulario para agregar un nuevo libro.
    Guarda en MySQL y crea documento inicial en MongoDB.
    """
    if request.method == 'POST':
        # Obtener datos del formulario
        titulo = request.POST.get('titulo')
        autor = request.POST.get('autor')
        isbn = request.POST.get('isbn')
        fecha_publicacion = request.POST.get('fecha_publicacion')
        precio = request.POST.get('precio')
        stock = request.POST.get('stock', 0)
        descripcion = request.POST.get('descripcion', '')
        
        try:
            # Guardar en MySQL usando Django ORM
            libro = Libro.objects.create(
                titulo=titulo,
                autor=autor,
                isbn=isbn,
                fecha_publicacion=fecha_publicacion,
                precio=precio,
                stock=stock,
                descripcion=descripcion
            )
            
            # Crear documento inicial en MongoDB
            db.libros.insert_one({
                'libro_id': str(libro.id),
                'titulo': titulo,
                'autor': autor,
                'isbn': isbn,
                'fecha_agregado': datetime.now(),
                'metadata': {
                    'precio': float(precio),
                    'stock': int(stock)
                }
            })
            
            # Crear documento de estadísticas en MongoDB
            db.estadisticas.insert_one({
                'libro_id': str(libro.id),
                'titulo': titulo,
                'views': 0,
                'prestamos': 0,
                'calificacion_promedio': 0.0,
                'created_at': datetime.now()
            })
            
            # Registrar en logs
            db.logs.insert_one({
                'accion': 'crear_libro',
                'libro_id': str(libro.id),
                'libro_titulo': titulo,
                'timestamp': datetime.now()
            })
            
            messages.success(request, f'Libro "{titulo}" creado exitosamente')
            return redirect('lista_libros')
            
        except Exception as e:
            messages.error(request, f'Error al crear libro: {str(e)}')
    
    return render(request, 'libros/crear_libro.html')


# ==========================================
# VISTA 5: Editar Libro (CRUD - Update)
# ==========================================
def editar_libro(request, libro_id):
    """
    Formulario para modificar un libro existente.
    Actualiza MySQL y sincroniza cambios con MongoDB.
    """
    libro = get_object_or_404(Libro, id=libro_id)
    
    if request.method == 'POST':
        # Actualizar campos
        libro.titulo = request.POST.get('titulo')
        libro.autor = request.POST.get('autor')
        libro.isbn = request.POST.get('isbn')
        libro.fecha_publicacion = request.POST.get('fecha_publicacion')
        libro.precio = request.POST.get('precio')
        libro.stock = request.POST.get('stock', 0)
        libro.descripcion = request.POST.get('descripcion', '')
        
        try:
            # Guardar en MySQL
            libro.save()
            
            # Actualizar en MongoDB
            db.libros.update_one(
                {'libro_id': str(libro_id)},
                {'$set': {
                    'titulo': libro.titulo,
                    'autor': libro.autor,
                    'isbn': libro.isbn,
                    'metadata.precio': float(libro.precio),
                    'metadata.stock': int(libro.stock),
                    'updated_at': datetime.now()
                }}
            )
            
            # Actualizar título en estadísticas
            db.estadisticas.update_one(
                {'libro_id': str(libro_id)},
                {'$set': {'titulo': libro.titulo}}
            )
            
            # Registrar en logs
            db.logs.insert_one({
                'accion': 'editar_libro',
                'libro_id': str(libro_id),
                'libro_titulo': libro.titulo,
                'timestamp': datetime.now()
            })
            
            messages.success(request, f'Libro "{libro.titulo}" actualizado')
            return redirect('detalle_libro', libro_id=libro_id)
            
        except Exception as e:
            messages.error(request, f'Error al actualizar: {str(e)}')
    
    context = {'libro': libro}
    return render(request, 'libros/editar_libro.html', context)


# ==========================================
# VISTA 6: Eliminar Libro (CRUD - Delete)
# ==========================================
def eliminar_libro(request, libro_id):
    """
    Elimina un libro del sistema.
    Borra de MySQL y archiva en MongoDB para auditoría.
    """
    libro = get_object_or_404(Libro, id=libro_id)
    
    if request.method == 'POST':
        titulo = libro.titulo
        
        try:
            # Archivar en MongoDB antes de eliminar (para auditoría)
            db.libros_eliminados.insert_one({
                'libro_id': str(libro_id),
                'titulo': titulo,
                'autor': libro.autor,
                'isbn': libro.isbn,
                'fecha_eliminacion': datetime.now(),
                'motivo': request.POST.get('motivo', 'No especificado')
            })
            
            # Eliminar de MySQL
            libro.delete()
            
            # Marcar como eliminado en MongoDB (mantener stats)
            db.estadisticas.update_one(
                {'libro_id': str(libro_id)},
                {'$set': {'eliminado': True, 'fecha_eliminacion': datetime.now()}}
            )
            
            # Registrar en logs
            db.logs.insert_one({
                'accion': 'eliminar_libro',
                'libro_id': str(libro_id),
                'libro_titulo': titulo,
                'timestamp': datetime.now()
            })
            
            messages.success(request, f'Libro "{titulo}" eliminado correctamente')
            return redirect('lista_libros')
            
        except Exception as e:
            messages.error(request, f'Error al eliminar: {str(e)}')
    
    context = {'libro': libro}
    return render(request, 'libros/confirmar_eliminacion.html', context)


# ==========================================
# VISTA 7: Estadísticas y Dashboard
# ==========================================
def estadisticas(request):
    """
    Dashboard con métricas y gráficas.
    Combina datos agregados de MySQL y MongoDB.
    """
    # Estadísticas de MySQL
    from django.db.models import Sum
    total_libros = Libro.objects.count()
    total_stock = Libro.objects.aggregate(Sum('stock'))['stock__sum'] or 0
    
    # Estadísticas de MongoDB - Top 10 más vistos
    top_vistos = list(db.estadisticas.find(
        {'eliminado': {'$ne': True}}
    ).sort('views', -1).limit(10))
    
    # Estadísticas de MongoDB - Top 10 más prestados
    top_prestados = list(db.estadisticas.find(
        {'eliminado': {'$ne': True}}
    ).sort('prestamos', -1).limit(10))
    
    # Total de views (suma de todas las visualizaciones)
    pipeline = [
        {'$match': {'eliminado': {'$ne': True}}},
        {'$group': {'_id': None, 'total_views': {'$sum': '$views'}}}
    ]
    total_views_result = list(db.estadisticas.aggregate(pipeline))
    total_views = total_views_result[0]['total_views'] if total_views_result else 0
    
    # Actividad reciente (últimos 20 logs)
    logs_recientes = list(db.logs.find().sort('timestamp', -1).limit(20))
    
    # Registrar acceso al dashboard
    db.logs.insert_one({
        'accion': 'ver_estadisticas',
        'timestamp': datetime.now()
    })
    
    context = {
        'total_libros': total_libros,
        'total_stock': total_stock,
        'total_views': total_views,
        'top_vistos': top_vistos,
        'top_prestados': top_prestados,
        'logs_recientes': logs_recientes
    }
    
    return render(request, 'libros/estadisticas.html', context)

📄 ARCHIVO: libros/mongo_utils.py

Utilidad para gestionar conexión a MongoDB:

# ========================================
# ARCHIVO: libros/mongo_utils.py
# Utilidades para conexión a MongoDB
# ========================================

from pymongo import MongoClient
from django.conf import settings

_mongo_client = None
_mongo_db = None

def get_mongo_connection():
    """
    Obtiene cliente de MongoDB (singleton).
    Se conecta solo una vez y reutiliza la conexión.
    """
    global _mongo_client
    
    if _mongo_client is None:
        connection_string = settings.MONGODB_CONNECTION_STRING
        _mongo_client = MongoClient(connection_string)
    
    return _mongo_client

def get_mongo_db():
    """
    Obtiene base de datos MongoDB configurada.
    """
    global _mongo_db
    
    if _mongo_db is None:
        client = get_mongo_connection()
        _mongo_db = client[settings.MONGODB_DATABASE_NAME]
    
    return _mongo_db

def close_mongo_connection():
    """
    Cierra conexión a MongoDB.
    Llamar al finalizar la aplicación.
    """
    global _mongo_client, _mongo_db
    
    if _mongo_client is not None:
        _mongo_client.close()
        _mongo_client = None
        _mongo_db = None

📄 ARCHIVO COMPLETO: biblioteca/urls.py

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── urls.py  ← ESTE ARCHIVO
# ========================================
# ARCHIVO: biblioteca/urls.py
# Configuración de rutas URL
# ========================================

from django.urls import path
from . import views

urlpatterns = [
    # Página principal
    path('', views.inicio, name='inicio'),
    
    # CRUD de libros
    path('lista/', views.lista_libros, name='lista_libros'),
    path('detalle//', views.detalle_libro, name='detalle_libro'),
    path('crear/', views.crear_libro, name='crear_libro'),
    path('editar//', views.editar_libro, name='editar_libro'),
    path('eliminar//', views.eliminar_libro, name='eliminar_libro'),
    
    # Dashboard
    path('estadisticas/', views.estadisticas, name='estadisticas'),
]

⚙️ Configuración en settings.py

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca_project/
    └── settings.py  ← AGREGAR CONFIGURACIÓN AQUÍ
# ========================================
# Agregar a biblioteca_project/settings.py
# ========================================

# Configuración de MongoDB
MONGODB_CONNECTION_STRING = 'mongodb+srv://biblioteca_admin:Biblioteca2026!@bibliotecacluster.xxxxx.mongodb.net/biblioteca_logs?retryWrites=true&w=majority'
MONGODB_DATABASE_NAME = 'biblioteca_logs'

# IMPORTANTE: En producción, usa variables de entorno:
# import os
# MONGODB_CONNECTION_STRING = os.environ.get('MONGODB_URI')
# MONGODB_DATABASE_NAME = os.environ.get('MONGODB_DB', 'biblioteca_logs')

⚠️ Seguridad

NUNCA subas el connection string con contraseñas reales a GitHub.

Usa variables de entorno en producción y archivos .env para desarrollo.

📄 TEMPLATES HTML COMPLETOS

📂 Estructura de Carpetas

biblioteca_project/
│
├── libros/
│   ├── templates/
│   │   └── libros/
│   │       ├── base.html              ← Template base (reutilizable)
│   │       ├── inicio.html            ← Página principal
│   │       ├── lista_libros.html      ← Catálogo de libros
│   │       ├── detalle_libro.html     ← Vista individual
│   │       ├── crear_libro.html       ← Formulario crear
│   │       ├── editar_libro.html      ← Formulario editar
│   │       ├── confirmar_eliminacion.html ← Confirmación borrado
│   │       └── estadisticas.html      ← Dashboard con gráficas
│   │
│   ├── views.py
│   ├── models.py
│   └── urls.py
└── ...

📄 TEMPLATE 1: base.html

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── base.html  ← ESTE ARCHIVO

Plantilla base reutilizada por todos los demás templates

<!-- ======================================== -->
<!-- ARCHIVO: biblioteca/templates/biblioteca/base.html -->
<!-- Template base con navbar y estilos -->
<!-- ======================================== -->

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Sistema Biblioteca{% endblock %}</title>
    
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Chart.js para gráficas -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    
    <style>
        :root {
            --primary-color: #f093fb;
            --secondary-color: #f5576c;
            --dark-color: #2c3e50;
            --light-bg: #f8f9fa;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
            min-height: 100vh;
        }
        
        .navbar {
            background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)) !important;
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
        }
        
        .navbar-brand {
            font-weight: bold;
            font-size: 1.5em;
        }
        
        .main-container {
            background: white;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            margin: 30px auto;
            padding: 40px;
            max-width: 1200px;
        }
        
        .card {
            border: none;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            transition: transform 0.3s;
        }
        
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 20px rgba(0,0,0,0.15);
        }
        
        .btn-primary {
            background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
            border: none;
            padding: 10px 25px;
            border-radius: 8px;
            font-weight: 600;
            transition: 0.3s;
        }
        
        .btn-primary:hover {
            transform: scale(1.05);
            box-shadow: 0 5px 15px rgba(240, 147, 251, 0.4);
        }
        
        .badge-stat {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 8px 15px;
            border-radius: 20px;
            font-size: 0.9em;
        }
        
        footer {
            background: var(--dark-color);
            color: white;
            padding: 30px 0;
            margin-top: 50px;
            text-align: center;
        }
    </style>
    
    {% block extra_css %}{% endblock %}
</head>
<body>
    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-dark">
        <div class="container-fluid">
            <a class="navbar-brand" href="{% url 'inicio' %}">
                📚 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' %}">🏠 Inicio</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'lista_libros' %}">📖 Catálogo</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'crear_libro' %}">➕ Agregar Libro</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'estadisticas' %}">📊 Estadísticas</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- Mensajes de Django -->
    {% if messages %}
        <div class="container mt-4">
            {% 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 %}

    <!-- Contenido principal -->
    <div class="main-container">
        {% block content %}{% endblock %}
    </div>

    <!-- Footer -->
    <footer>
        <div class="container">
            <p><strong>Universidad Tecnológica de Hermosillo</strong></p>
            <p>Sistema Híbrido de Biblioteca - MySQL + MongoDB</p>
            <p style="opacity: 0.7; margin-top: 15px;">© 2026 UTH - Servicios Web</p>
        </div>
    </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>

📄 TEMPLATE 2: inicio.html

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── inicio.html  ← ESTE ARCHIVO

Página principal con estadísticas generales

<!-- ======================================== -->
<!-- ARCHIVO: biblioteca/templates/biblioteca/inicio.html -->
<!-- Página principal con resumen del sistema -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Inicio - Sistema Biblioteca{% endblock %}

{% block content %}
<div class="text-center mb-5">
    <h1 class="display-3">📚 Bienvenido al Sistema de Biblioteca</h1>
    <p class="lead text-muted">Sistema Híbrido MySQL + MongoDB</p>
</div>

<!-- Estadísticas Generales -->
<div class="row mb-5">
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #f093fb; font-size: 3em;">{{ total_libros_mysql }}</h2>
                <p class="text-muted">Libros en MySQL</p>
                <small class="badge bg-info">Base de Datos SQL</small>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #f5576c; font-size: 3em;">{{ total_libros_mongo }}</h2>
                <p class="text-muted">Documentos en MongoDB</p>
                <small class="badge bg-success">Base de Datos NoSQL</small>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #667eea; font-size: 3em;">2</h2>
                <p class="text-muted">Bases de Datos Activas</p>
                <small class="badge bg-warning text-dark">Sistema Híbrido</small>
            </div>
        </div>
    </div>
</div>

<!-- Top Libros Más Vistos -->
<div class="card mb-4">
    <div class="card-header" style="background: linear-gradient(90deg, #f093fb, #f5576c); color: white;">
        <h3>🔥 Top 5 Libros Más Vistos</h3>
    </div>
    <div class="card-body">
        {% if top_libros %}
            <div class="list-group">
                {% for libro in top_libros %}
                    <div class="list-group-item d-flex justify-content-between align-items-center">
                        <div>
                            <h5>{{ forloop.counter }}. {{ libro.titulo }}</h5>
                            <small class="text-muted">Libro ID: {{ libro.libro_id }}</small>
                        </div>
                        <span class="badge-stat">👁️ {{ libro.views }} vistas</span>
                    </div>
                {% endfor %}
            </div>
        {% else %}
            <p class="text-muted text-center">No hay datos de estadísticas aún</p>
        {% endif %}
    </div>
</div>

<!-- Acciones Rápidas -->
<div class="row">
    <div class="col-md-6 mb-3">
        <div class="card h-100">
            <div class="card-body text-center">
                <div style="font-size: 4em; margin-bottom: 20px;">📖</div>
                <h4>Ver Catálogo</h4>
                <p class="text-muted">Explora todos los libros disponibles</p>
                <a href="{% url 'lista_libros' %}" class="btn btn-primary">Ir al Catálogo</a>
            </div>
        </div>
    </div>
    
    <div class="col-md-6 mb-3">
        <div class="card h-100">
            <div class="card-body text-center">
                <div style="font-size: 4em; margin-bottom: 20px;">📊</div>
                <h4>Ver Estadísticas</h4>
                <p class="text-muted">Dashboard con métricas y gráficas</p>
                <a href="{% url 'estadisticas' %}" class="btn btn-primary">Ver Dashboard</a>
            </div>
        </div>
    </div>
</div>
{% endblock %}

📄 TEMPLATE 3: lista_libros.html

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── lista_libros.html  ← ESTE ARCHIVO

Catálogo de libros con estadísticas de MongoDB

<!-- ======================================== -->
<!-- ARCHIVO: biblioteca/templates/biblioteca/lista_libros.html -->
<!-- Catálogo completo de libros -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Catálogo de Libros{% endblock %}

{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
    <h1>📖 Catálogo de Libros</h1>
    <a href="{% url 'crear_libro' %}" class="btn btn-primary">➕ Agregar Nuevo Libro</a>
</div>

{% if libros_con_stats %}
    <div class="row">
        {% for item in libros_con_stats %}
            <div class="col-md-4 mb-4">
                <div class="card h-100">
                    <div class="card-body">
                        <h5 class="card-title">{{ item.libro.titulo }}</h5>
                        <p class="card-text">
                            <strong>Autor:</strong> {{ item.libro.autor }}<br>
                            <strong>ISBN:</strong> {{ item.libro.isbn }}<br>
                            <strong>Precio:</strong> ${{ item.libro.precio }}<br>
                            <strong>Stock:</strong> {{ item.libro.stock }} unidades
                        </p>
                        
                        <!-- Estadísticas de MongoDB -->
                        <div class="d-flex justify-content-between mt-3">
                            <span class="badge bg-info">👁️ {{ item.views }} vistas</span>
                            <span class="badge bg-success">📚 {{ item.prestamos }} préstamos</span>
                        </div>
                        
                        {% if item.calificacion > 0 %}
                            <div class="mt-2">
                                <span class="badge bg-warning text-dark">⭐ {{ item.calificacion }}/5.0</span>
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="card-footer bg-transparent">
                        <a href="{% url 'detalle_libro' item.libro.id %}" class="btn btn-sm btn-outline-primary">Ver Detalles</a>
                        <a href="{% url 'editar_libro' item.libro.id %}" class="btn btn-sm btn-outline-secondary">✏️ Editar</a>
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
{% else %}
    <div class="alert alert-info text-center">
        <h4>📭 No hay libros en el catálogo</h4>
        <p>Comienza agregando tu primer libro al sistema.</p>
        <a href="{% url 'crear_libro' %}" class="btn btn-primary">➕ Agregar Primer Libro</a>
    </div>
{% endif %}
{% endblock %}

📄 TEMPLATE 4: detalle_libro.html

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── detalle_libro.html  ← ESTE ARCHIVO

Vista detallada de un libro individual

<!-- ======================================== -->
<!-- ARCHIVO: biblioteca/templates/biblioteca/detalle_libro.html -->
<!-- Vista individual de un libro -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}{{ libro.titulo }} - Detalle{% endblock %}

{% block content %}
<div class="mb-4">
    <a href="{% url 'lista_libros' %}" class="btn btn-outline-secondary">← Volver al Catálogo</a>
</div>

<div class="row">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header" style="background: linear-gradient(90deg, #f093fb, #f5576c); color: white;">
                <h2>📖 {{ libro.titulo }}</h2>
            </div>
            <div class="card-body">
                <table class="table table-borderless">
                    <tbody>
                        <tr>
                            <th width="200">📝 Título:</th>
                            <td><strong>{{ libro.titulo }}</strong></td>
                        </tr>
                        <tr>
                            <th>✍️ Autor:</th>
                            <td>{{ libro.autor }}</td>
                        </tr>
                        <tr>
                            <th>🔢 ISBN:</th>
                            <td><code>{{ libro.isbn }}</code></td>
                        </tr>
                        <tr>
                            <th>📅 Fecha Publicación:</th>
                            <td>{{ libro.fecha_publicacion }}</td>
                        </tr>
                        <tr>
                            <th>💰 Precio:</th>
                            <td><span class="badge bg-success">${{ libro.precio }}</span></td>
                        </tr>
                        <tr>
                            <th>📦 Stock:</th>
                            <td>
                                {% if libro.stock > 0 %}
                                    <span class="badge bg-info">{{ libro.stock }} unidades</span>
                                {% else %}
                                    <span class="badge bg-danger">Agotado</span>
                                {% endif %}
                            </td>
                        </tr>
                    </tbody>
                </table>
                
                {% if libro.descripcion %}
                    <hr>
                    <h5>📄 Descripción:</h5>
                    <p>{{ libro.descripcion }}</p>
                {% endif %}
            </div>
        </div>
        
        <!-- Botones de Acción -->
        <div class="mt-4">
            <a href="{% url 'editar_libro' libro.id %}" class="btn btn-primary">✏️ Editar Libro</a>
            <a href="{% url 'eliminar_libro' libro.id %}" class="btn btn-danger">🗑️ Eliminar Libro</a>
        </div>
    </div>
    
    <div class="col-md-4">
        <!-- Estadísticas de MongoDB -->
        <div class="card mb-3">
            <div class="card-header bg-info text-white">
                <h5>📊 Estadísticas (MongoDB)</h5>
            </div>
            <div class="card-body text-center">
                <div class="mb-3">
                    <h2 style="color: #f093fb;">{{ views }}</h2>
                    <p class="text-muted">👁️ Vistas Totales</p>
                </div>
                
                <div>
                    <h2 style="color: #f5576c;">{{ prestamos }}</h2>
                    <p class="text-muted">📚 Préstamos Realizados</p>
                </div>
            </div>
        </div>
        
        <!-- Info del Sistema -->
        <div class="card">
            <div class="card-header bg-secondary text-white">
                <h5>💾 Información de Almacenamiento</h5>
            </div>
            <div class="card-body">
                <p><strong>Datos básicos:</strong> MySQL</p>
                <p><strong>Estadísticas:</strong> MongoDB</p>
                <p><strong>ID MySQL:</strong> {{ libro.id }}</p>
                <small class="text-muted">Sistema Híbrido Activo</small>
            </div>
        </div>
    </div>
</div>
{% endblock %}

📄 TEMPLATE 5: crear_libro.html

Formulario para agregar un nuevo libro

<!-- ======================================== -->
<!-- ARCHIVO: libros/templates/libros/crear_libro.html -->
<!-- Formulario para crear nuevo libro -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Agregar Nuevo Libro{% endblock %}

{% block content %}
<div class="mb-4">
    <a href="{% url 'lista_libros' %}" class="btn btn-outline-secondary">← Volver al Catálogo</a>
</div>

<div class="card">
    <div class="card-header" style="background: linear-gradient(90deg, #f093fb, #f5576c); color: white;">
        <h2>➕ Agregar Nuevo Libro</h2>
    </div>
    <div class="card-body">
        <form method="post" action="{% url 'crear_libro' %}">
            {% csrf_token %}
            
            <div class="row">
                <div class="col-md-6 mb-3">
                    <label for="titulo" class="form-label">📝 Título *</label>
                    <input type="text" class="form-control" id="titulo" name="titulo" required>
                </div>
                
                <div class="col-md-6 mb-3">
                    <label for="autor" class="form-label">✍️ Autor *</label>
                    <input type="text" class="form-control" id="autor" name="autor" required>
                </div>
            </div>
            
            <div class="row">
                <div class="col-md-4 mb-3">
                    <label for="isbn" class="form-label">🔢 ISBN *</label>
                    <input type="text" class="form-control" id="isbn" name="isbn" 
                           placeholder="978-3-16-148410-0" required>
                </div>
                
                <div class="col-md-4 mb-3">
                    <label for="fecha_publicacion" class="form-label">📅 Fecha Publicación *</label>
                    <input type="date" class="form-control" id="fecha_publicacion" name="fecha_publicacion" required>
                </div>
                
                <div class="col-md-4 mb-3">
                    <label for="precio" class="form-label">💰 Precio *</label>
                    <input type="number" step="0.01" class="form-control" id="precio" name="precio" 
                           placeholder="0.00" required>
                </div>
            </div>
            
            <div class="mb-3">
                <label for="stock" class="form-label">📦 Stock</label>
                <input type="number" class="form-control" id="stock" name="stock" 
                       placeholder="0" value="1">
            </div>
            
            <div class="mb-3">
                <label for="descripcion" class="form-label">📄 Descripción</label>
                <textarea class="form-control" id="descripcion" name="descripcion" 
                          rows="4" placeholder="Descripción opcional del libro..."></textarea>
            </div>
            
            <hr>
            
            <div class="alert alert-info">
                <strong>💡 Nota:</strong> Al crear el libro:
                <ul>
                    <li>Se guardará en <strong>MySQL</strong> (datos estructurados)</li>
                    <li>Se creará documento en <strong>MongoDB</strong> (estadísticas iniciales)</li>
                    <li>Se registrará en <strong>logs de MongoDB</strong> (auditoría)</li>
                </ul>
            </div>
            
            <div class="d-flex justify-content-between">
                <a href="{% url 'lista_libros' %}" class="btn btn-secondary">Cancelar</a>
                <button type="submit" class="btn btn-primary">💾 Guardar Libro</button>
            </div>
        </form>
    </div>
</div>
{% endblock %}

📄 TEMPLATE 6: editar_libro.html

Formulario para modificar un libro existente

<!-- ======================================== -->
<!-- ARCHIVO: libros/templates/libros/editar_libro.html -->
<!-- Formulario para editar libro existente -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Editar: {{ libro.titulo }}{% endblock %}

{% block content %}
<div class="mb-4">
    <a href="{% url 'detalle_libro' libro.id %}" class="btn btn-outline-secondary">← Volver a Detalles</a>
</div>

<div class="card">
    <div class="card-header" style="background: linear-gradient(90deg, #f093fb, #f5576c); color: white;">
        <h2>✏️ Editar Libro: {{ libro.titulo }}</h2>
    </div>
    <div class="card-body">
        <form method="post" action="{% url 'editar_libro' libro.id %}">
            {% csrf_token %}
            
            <div class="row">
                <div class="col-md-6 mb-3">
                    <label for="titulo" class="form-label">📝 Título *</label>
                    <input type="text" class="form-control" id="titulo" name="titulo" 
                           value="{{ libro.titulo }}" required>
                </div>
                
                <div class="col-md-6 mb-3">
                    <label for="autor" class="form-label">✍️ Autor *</label>
                    <input type="text" class="form-control" id="autor" name="autor" 
                           value="{{ libro.autor }}" required>
                </div>
            </div>
            
            <div class="row">
                <div class="col-md-4 mb-3">
                    <label for="isbn" class="form-label">🔢 ISBN *</label>
                    <input type="text" class="form-control" id="isbn" name="isbn" 
                           value="{{ libro.isbn }}" required>
                </div>
                
                <div class="col-md-4 mb-3">
                    <label for="fecha_publicacion" class="form-label">📅 Fecha Publicación *</label>
                    <input type="date" class="form-control" id="fecha_publicacion" name="fecha_publicacion" 
                           value="{{ libro.fecha_publicacion|date:'Y-m-d' }}" required>
                </div>
                
                <div class="col-md-4 mb-3">
                    <label for="precio" class="form-label">💰 Precio *</label>
                    <input type="number" step="0.01" class="form-control" id="precio" name="precio" 
                           value="{{ libro.precio }}" required>
                </div>
            </div>
            
            <div class="mb-3">
                <label for="stock" class="form-label">📦 Stock</label>
                <input type="number" class="form-control" id="stock" name="stock" 
                       value="{{ libro.stock }}">
            </div>
            
            <div class="mb-3">
                <label for="descripcion" class="form-label">📄 Descripción</label>
                <textarea class="form-control" id="descripcion" name="descripcion" 
                          rows="4">{{ libro.descripcion }}</textarea>
            </div>
            
            <hr>
            
            <div class="alert alert-warning">
                <strong>⚠️ Importante:</strong> Al actualizar el libro:
                <ul>
                    <li>Se modificará en <strong>MySQL</strong> (datos principales)</li>
                    <li>Se sincronizará con <strong>MongoDB</strong> (metadata)</li>
                    <li>Se mantendrán las estadísticas existentes</li>
                </ul>
            </div>
            
            <div class="d-flex justify-content-between">
                <a href="{% url 'detalle_libro' libro.id %}" class="btn btn-secondary">Cancelar</a>
                <button type="submit" class="btn btn-primary">💾 Guardar Cambios</button>
            </div>
        </form>
    </div>
</div>
{% endblock %}

📄 TEMPLATE 7: confirmar_eliminacion.html

Confirmación antes de eliminar un libro

<!-- ======================================== -->
<!-- ARCHIVO: libros/templates/libros/confirmar_eliminacion.html -->
<!-- Confirmación de eliminación de libro -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Confirmar Eliminación{% endblock %}

{% block content %}
<div class="mb-4">
    <a href="{% url 'detalle_libro' libro.id %}" class="btn btn-outline-secondary">← Volver a Detalles</a>
</div>

<div class="card border-danger">
    <div class="card-header bg-danger text-white">
        <h2>⚠️ Confirmar Eliminación</h2>
    </div>
    <div class="card-body">
        <div class="alert alert-danger">
            <h4>🗑️ ¿Estás seguro de eliminar este libro?</h4>
            <p>Esta acción <strong>NO se puede deshacer</strong>.</p>
        </div>
        
        <h5>Información del Libro a Eliminar:</h5>
        <table class="table table-bordered">
            <tbody>
                <tr>
                    <th width="200">Título:</th>
                    <td><strong>{{ libro.titulo }}</strong></td>
                </tr>
                <tr>
                    <th>Autor:</th>
                    <td>{{ libro.autor }}</td>
                </tr>
                <tr>
                    <th>ISBN:</th>
                    <td>{{ libro.isbn }}</td>
                </tr>
                <tr>
                    <th>Stock:</th>
                    <td>{{ libro.stock }} unidades</td>
                </tr>
            </tbody>
        </table>
        
        <form method="post" action="{% url 'eliminar_libro' libro.id %}">
            {% csrf_token %}
            
            <div class="mb-3">
                <label for="motivo" class="form-label">Motivo de Eliminación (Opcional):</label>
                <textarea class="form-control" id="motivo" name="motivo" rows="3" 
                          placeholder="Ej: Libro descontinuado, duplicado, etc."></textarea>
            </div>
            
            <hr>
            
            <div class="alert alert-info">
                <strong>📝 Nota:</strong> Al eliminar el libro:
                <ul>
                    <li>Se borrará de <strong>MySQL</strong> (datos principales)</li>
                    <li>Se archivará en <strong>MongoDB</strong> (para auditoría)</li>
                    <li>Las estadísticas se marcarán como "eliminado"</li>
                    <li>Se registrará en logs con motivo de eliminación</li>
                </ul>
            </div>
            
            <div class="d-flex justify-content-between">
                <a href="{% url 'detalle_libro' libro.id %}" class="btn btn-secondary">❌ Cancelar</a>
                <button type="submit" class="btn btn-danger">🗑️ Sí, Eliminar Definitivamente</button>
            </div>
        </form>
    </div>
</div>
{% endblock %}

📄 TEMPLATE 8: estadisticas.html

Dashboard con gráficas y métricas del sistema

<!-- ======================================== -->
<!-- ARCHIVO: libros/templates/libros/estadisticas.html -->
<!-- Dashboard de estadísticas con gráficas -->
<!-- ======================================== -->

{% extends 'libros/base.html' %}

{% block title %}Estadísticas del Sistema{% endblock %}

{% block content %}
<h1 class="mb-4">📊 Dashboard de Estadísticas</h1>

<!-- Resumen General -->
<div class="row mb-4">
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #f093fb;">{{ total_libros }}</h2>
                <p class="text-muted">Total Libros (MySQL)</p>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #f5576c;">{{ total_stock }}</h2>
                <p class="text-muted">Stock Total</p>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card text-center">
            <div class="card-body">
                <h2 style="color: #667eea;">{{ total_views }}</h2>
                <p class="text-muted">Vistas Totales (MongoDB)</p>
            </div>
        </div>
    </div>
</div>

<!-- Gráficas -->
<div class="row mb-4">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header bg-info text-white">
                <h5>👁️ Top 10 Más Vistos</h5>
            </div>
            <div class="card-body">
                <canvas id="chartVistos"></canvas>
            </div>
        </div>
    </div>
    
    <div class="col-md-6">
        <div class="card">
            <div class="card-header bg-success text-white">
                <h5>📚 Top 10 Más Prestados</h5>
            </div>
            <div class="card-body">
                <canvas id="chartPrestados"></canvas>
            </div>
        </div>
    </div>
</div>

<!-- Actividad Reciente -->
<div class="card">
    <div class="card-header bg-warning">
        <h5>📝 Actividad Reciente (MongoDB Logs)</h5>
    </div>
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th>Acción</th>
                        <th>Libro</th>
                        <th>Fecha y Hora</th>
                    </tr>
                </thead>
                <tbody>
                    {% for log in logs_recientes %}
                        <tr>
                            <td>
                                {% if log.accion == 'crear_libro' %}
                                    <span class="badge bg-success">➕ Crear</span>
                                {% elif log.accion == 'editar_libro' %}
                                    <span class="badge bg-primary">✏️ Editar</span>
                                {% elif log.accion == 'eliminar_libro' %}
                                    <span class="badge bg-danger">🗑️ Eliminar</span>
                                {% elif log.accion == 'ver_detalle' %}
                                    <span class="badge bg-info">👁️ Ver</span>
                                {% else %}
                                    <span class="badge bg-secondary">{{ log.accion }}</span>
                                {% endif %}
                            </td>
                            <td>{{ log.libro_titulo|default:"N/A" }}</td>
                            <td><small>{{ log.timestamp|date:"d/m/Y H:i:s" }}</small></td>
                        </tr>
                    {% empty %}
                        <tr>
                            <td colspan="3" class="text-center text-muted">No hay actividad registrada</td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
{% endblock %}

        

📦 TEMPLATES DEL SISTEMA HÍBRIDO (MySQL + MongoDB)

🎯 Templates Completos para el Sistema Híbrido

Estos templates funcionan con las views híbridas que conectan MySQL y MongoDB Atlas usando el campo común libro_id

📄 TEMPLATE 5: dashboard.html (Estadísticas Híbridas)

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── dashboard.html  ← ESTE ARCHIVO
<!-- ARCHIVO: biblioteca/templates/biblioteca/dashboard.html -->
{% extends 'biblioteca/base.html' %}

{% block title %}Dashboard - Biblioteca Híbrida{% endblock %}

{% block content %}
<div class="row">
    <div class="col-12">
        <h1 class="mb-4">📊 Dashboard del Sistema</h1>
        <p class="text-muted">Estadísticas combinadas de MySQL y MongoDB Atlas</p>
    </div>
</div>

<!-- Tarjetas de Estadísticas -->
<div class="row mb-4">
    <!-- MySQL Stats -->
    <div class="col-md-3">
        <div class="card border-primary">
            <div class="card-body text-center">
                <span class="badge badge-mysql mb-2">MySQL</span>
                <h2 class="text-primary">{{ total_prestamos_activos }}</h2>
                <p class="mb-0">Préstamos Activos</p>
            </div>
        </div>
    </div>
    
    <div class="col-md-3">
        <div class="card border-info">
            <div class="card-body text-center">
                <span class="badge badge-mysql mb-2">MySQL</span>
                <h2 class="text-info">{{ total_prestamos_historico }}</h2>
                <p class="mb-0">Total Préstamos</p>
            </div>
        </div>
    </div>
    
    <!-- MongoDB Stats -->
    <div class="col-md-3">
        <div class="card border-success">
            <div class="card-body text-center">
                <span class="badge badge-mongodb mb-2">MongoDB</span>
                <h2 class="text-success">{{ total_libros }}</h2>
                <p class="mb-0">Libros en Catálogo</p>
            </div>
        </div>
    </div>
    
    <div class="col-md-3">
        <div class="card border-warning">
            <div class="card-body text-center">
                <span class="badge badge-mongodb mb-2">MongoDB</span>
                <h2 class="text-warning">{{ libros_disponibles }}</h2>
                <p class="mb-0">Libros Disponibles</p>
            </div>
        </div>
    </div>
</div>

<!-- Libros Más Prestados (MongoDB Estadísticas) -->
{% if tendencia %}
<div class="row">
    <div class="col-12">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h4 class="mb-0"><i class="bi bi-graph-up"></i> Libros Más Prestados - {{ tendencia.periodo }}</h4>
                <span class="badge badge-mongodb">MongoDB Estadísticas</span>
            </div>
            <div class="card-body">
                <div class="table-responsive">
                    <table class="table table-striped">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Libro ID</th>
                                <th>Cantidad de Préstamos</th>
                                <th>Acciones</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for item in tendencia.libros_mas_prestados %}
                            <tr>
                                <td>{{ forloop.counter }}</td>
                                <td><span class="badge bg-info">ID: {{ item.libro_id }}</span></td>
                                <td>{{ item.cantidad }} préstamos</td>
                                <td>
                                    <a href="{% url 'detalle_libro' item.libro_id %}" class="btn btn-sm btn-outline-primary">
                                        Ver Detalle
                                    </a>
                                </td>
                            </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>
                
                <div class="mt-3">
                    <h5>📚 Categorías Populares:</h5>
                    {% for categoria in tendencia.categorias_populares %}
                        <span class="badge bg-secondary me-2">{{ categoria }}</span>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
</div>
{% endif %}

<!-- Acciones Rápidas -->
<div class="row mt-4">
    <div class="col-12">
        <div class="card">
            <div class="card-header">
                <h4 class="mb-0">⚡ Acciones Rápidas</h4>
            </div>
            <div class="card-body text-center">
                <a href="{% url 'listar_libros' %}" class="btn btn-primary btn-lg me-2">
                    <i class="bi bi-book"></i> Ver Catálogo de Libros
                </a>
                <a href="{% url 'mis_prestamos' %}" class="btn btn-success btn-lg">
                    <i class="bi bi-list-check"></i> Mis Préstamos
                </a>
            </div>
        </div>
    </div>
</div>
{% endblock %}
    

📄 TEMPLATE 6: listar_libros.html (MongoDB)

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── listar_libros.html  ← ESTE ARCHIVO
<!-- ARCHIVO: biblioteca/templates/biblioteca/listar_libros.html -->
{% extends 'biblioteca/base.html' %}

{% block title %}Catálogo de Libros{% endblock %}

{% block content %}
<div class="row mb-4">
    <div class="col-md-8">
        <h1>📚 Catálogo de Libros</h1>
        <p class="text-muted">
            <span class="badge badge-mongodb">MongoDB Atlas</span>
            Libros almacenados en la nube
        </p>
    </div>
    <div class="col-md-4">
        <form method="GET" class="input-group">
            <input type="text" name="q" class="form-control" 
                   placeholder="Buscar por título o autor..." 
                   value="{{ query }}">
            <button class="btn btn-primary" type="submit">
                <i class="bi bi-search"></i> Buscar
            </button>
        </form>
    </div>
</div>

<div class="alert alert-info">
    <i class="bi bi-info-circle"></i> 
    Total de libros encontrados: <strong>{{ total_libros }}</strong>
</div>

<div class="row">
    {% for libro in libros %}
    <div class="col-md-4 mb-4">
        <div class="card h-100 shadow-sm">
            <div class="card-header bg-gradient" 
                 style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
                <h5 class="mb-0">
                    <span class="badge bg-light text-dark">ID: {{ libro.libro_id }}</span>
                </h5>
            </div>
            <div class="card-body">
                <h5 class="card-title">{{ libro.titulo }}</h5>
                <p class="card-text">
                    <strong>Autor:</strong> {{ libro.autor.nombre }}<br>
                    {% if libro.autor.pais %}
                    <small class="text-muted">{{ libro.autor.pais }}</small>
                    {% endif %}
                </p>
                
                {% if libro.categorias %}
                <div class="mb-2">
                    {% for cat in libro.categorias %}
                    <span class="badge bg-secondary">{{ cat }}</span>
                    {% endfor %}
                </div>
                {% endif %}
                
                <div class="mt-3">
                    <p class="mb-1">
                        <strong>Stock:</strong> 
                        {% if libro.stock > 0 %}
                            <span class="badge bg-success">{{ libro.stock }} disponibles</span>
                        {% else %}
                            <span class="badge bg-danger">Agotado</span>
                        {% endif %}
                    </p>
                    {% if libro.precio %}
                    <p class="mb-1"><strong>Precio:</strong> ${{ libro.precio }}</p>
                    {% endif %}
                </div>
            </div>
            <div class="card-footer">
                <a href="{% url 'detalle_libro' libro.libro_id %}" 
                   class="btn btn-primary btn-sm w-100">
                    <i class="bi bi-eye"></i> Ver Detalles
                </a>
            </div>
        </div>
    </div>
    {% empty %}
    <div class="col-12">
        <div class="alert alert-warning text-center">
            <h4>📭 No se encontraron libros</h4>
            <p>Intenta con otra búsqueda o explora el catálogo completo.</p>
        </div>
    </div>
    {% endfor %}
</div>
{% endblock %}
    

📄 TEMPLATE 7: mis_prestamos.html (Consulta Híbrida - EL MÁS IMPORTANTE)

📂 Ruta completa del archivo:

biblioteca_project/
└── biblioteca/
    └── templates/
        └── biblioteca/
            └── mis_prestamos.html  ← ESTE ARCHIVO
<!-- ARCHIVO: biblioteca/templates/biblioteca/mis_prestamos.html -->
{% extends 'biblioteca/base.html' %}

{% block title %}Mis Préstamos{% endblock %}

{% block content %}
<div class="row mb-4">
    <div class="col-12">
        <h1>📚 Mis Préstamos</h1>
        <p class="text-muted">
            <span class="badge badge-mysql">MySQL</span> + 
            <span class="badge badge-mongodb">MongoDB</span>
            Datos combinados de ambas bases de datos
        </p>
    </div>
</div>

<div class="alert alert-info">
    <i class="bi bi-info-circle"></i> 
    Total de préstamos: <strong>{{ total_prestamos }}</strong>
</div>

<div class="row">
    {% for item in prestamos_completos %}
    <div class="col-md-6 mb-4">
        <div class="card h-100 shadow-sm">
            <div class="card-header" 
                 {% if item.prestamo.estado == 'ACTIVO' %}
                     style="background-color: #28a745; color: white;"
                 {% elif item.prestamo.estado == 'DEVUELTO' %}
                     style="background-color: #6c757d; color: white;"
                 {% else %}
                     style="background-color: #dc3545; color: white;"
                 {% endif %}>
                <div class="d-flex justify-content-between align-items-center">
                    <span>
                        <strong>Préstamo #{{ item.prestamo.id }}</strong>
                    </span>
                    <span class="badge bg-light text-dark">
                        {{ item.prestamo.estado }}
                    </span>
                </div>
            </div>
            <div class="card-body">
                <!-- Datos del Libro (MongoDB) -->
                <div class="mb-3">
                    <h5 class="card-title">
                        <span class="badge badge-mongodb">MongoDB</span>
                        {{ item.libro.titulo }}
                    </h5>
                    <p class="card-text mb-1">
                        <strong>Autor:</strong> {{ item.libro.autor.nombre|default:"N/A" }}
                    </p>
                    <p class="card-text mb-1">
                        <strong>Libro ID:</strong> 
                        <span class="badge bg-info">{{ item.prestamo.libro_id }}</span>
                        <small class="text-muted">(Campo común)</small>
                    </p>
                </div>
                
                <hr>
                
                <!-- Datos del Préstamo (MySQL) -->
                <div>
                    <p class="mb-1">
                        <span class="badge badge-mysql">MySQL</span>
                    </p>
                    <p class="mb-1">
                        <i class="bi bi-calendar-check"></i> 
                        <strong>Fecha de préstamo:</strong> 
                        {{ item.prestamo.fecha_prestamo|date:"d/m/Y H:i" }}
                    </p>
                    
                    {% if item.prestamo.fecha_devolucion %}
                    <p class="mb-1">
                        <i class="bi bi-calendar-x"></i> 
                        <strong>Fecha de devolución:</strong> 
                        {{ item.prestamo.fecha_devolucion|date:"d/m/Y H:i" }}
                    </p>
                    {% else %}
                    <p class="mb-1">
                        <i class="bi bi-clock"></i> 
                        <strong>Plazo:</strong> 
                        {{ item.prestamo.dias_prestamo }} días
                    </p>
                    {% endif %}
                </div>
            </div>
            
            {% if item.prestamo.estado == 'ACTIVO' %}
            <div class="card-footer">
                <form method="POST" action="{% url 'devolver_libro' item.prestamo.id %}" 
                      onsubmit="return confirm('¿Seguro que deseas devolver este libro?')">
                    {% csrf_token %}
                    <button type="submit" class="btn btn-warning w-100">
                        <i class="bi bi-arrow-return-left"></i> Devolver Libro
                    </button>
                </form>
                <small class="text-muted d-block mt-2">
                    Se actualizará MySQL y se restaurará stock en MongoDB
                </small>
            </div>
            {% endif %}
        </div>
    </div>
    {% empty %}
    <div class="col-12">
        <div class="alert alert-warning text-center">
            <h4>📭 No tienes préstamos registrados</h4>
            <p>Explora el catálogo y solicita tu primer libro.</p>
            <a href="{% url 'listar_libros' %}" class="btn btn-primary">
                Ver Catálogo
            </a>
        </div>
    </div>
    {% endfor %}
</div>

<!-- Explicación del Flujo Híbrido -->
<div class="card mt-4 border-info">
    <div class="card-header bg-info text-white">
        <h5 class="mb-0">🔍 ¿Cómo funciona esta vista híbrida?</h5>
    </div>
    <div class="card-body">
        <ol>
            <li><strong>MySQL:</strong> Se obtienen los préstamos con <code>Prestamo.objects.filter(usuario=tu_usuario)</code></li>
            <li><strong>Para cada préstamo:</strong>
                <ul>
                    <li>Se lee el campo <code>libro_id</code> desde MySQL</li>
                    <li>Se busca el libro en MongoDB usando: <code>libros.find_one({"libro_id": X})</code></li>
                    <li>Se combinan: fechas (MySQL) + título/autor (MongoDB)</li>
                </ul>
            </li>
            <li><strong>Resultado:</strong> Una vista con información de ambas bases de datos</li>
        </ol>
    </div>
</div>
{% endblock %}
    

📄 Template: biblioteca/templates/biblioteca/registrar_prestamo.html

Ruta completa: biblioteca_project/biblioteca/templates/biblioteca/registrar_prestamo.html

{% extends 'biblioteca/base.html' %}

{% block title %}Registrar Préstamo{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-8">
        <div class="card shadow">
            <div class="card-header bg-gradient" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
                <h2 class="text-white mb-0">📚 Registrar Nuevo Préstamo</h2>
            </div>
            <div class="card-body">
                <form method="post">
                    {% csrf_token %}
                    
                    <div class="mb-3">
                        <label for="libro_id" class="form-label">Libro ID</label>
                        <input type="text" class="form-control" id="libro_id" name="libro_id" required 
                               placeholder="Ingrese el ID del libro desde MongoDB">
                        <small class="form-text text-muted">Ejemplo: 6789abcd1234ef5678901234</small>
                    </div>

                    <div class="mb-3">
                        <label for="usuario" class="form-label">Usuario</label>
                        <input type="text" class="form-control" id="usuario" name="usuario" required 
                               placeholder="Nombre del usuario">
                    </div>

                    <div class="mb-3">
                        <label for="fecha_prestamo" class="form-label">Fecha de Préstamo</label>
                        <input type="date" class="form-control" id="fecha_prestamo" name="fecha_prestamo" required>
                    </div>

                    <div class="mb-3">
                        <label for="fecha_devolucion" class="form-label">Fecha de Devolución (opcional)</label>
                        <input type="date" class="form-control" id="fecha_devolucion" name="fecha_devolucion">
                    </div>

                    <div class="d-grid gap-2">
                        <button type="submit" class="btn btn-primary btn-lg">
                            💾 Registrar Préstamo
                        </button>
                        <a href="{% url 'mis_prestamos' %}" class="btn btn-secondary">Cancelar</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
    

📄 Template: biblioteca/templates/biblioteca/crear_libro.html

Ruta completa: biblioteca_project/biblioteca/templates/biblioteca/crear_libro.html

{% extends 'biblioteca/base.html' %}

{% block title %}Crear Libro{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-10">
        <div class="card shadow">
            <div class="card-header bg-success text-white">
                <h2 class="mb-0">➕ Agregar Nuevo Libro a MongoDB Atlas</h2>
            </div>
            <div class="card-body">
                <form method="post">
                    {% csrf_token %}
                    
                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="titulo" class="form-label">Título *</label>
                            <input type="text" class="form-control" id="titulo" name="titulo" required>
                        </div>

                        <div class="col-md-6 mb-3">
                            <label for="autor" class="form-label">Autor *</label>
                            <input type="text" class="form-control" id="autor" name="autor" required>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-4 mb-3">
                            <label for="isbn" class="form-label">ISBN *</label>
                            <input type="text" class="form-control" id="isbn" name="isbn" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="anio_publicacion" class="form-label">Año de Publicación *</label>
                            <input type="number" class="form-control" id="anio_publicacion" name="anio_publicacion" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="categoria" class="form-label">Categoría *</label>
                            <input type="text" class="form-control" id="categoria" name="categoria" required>
                        </div>
                    </div>

                    <div class="mb-3">
                        <label for="descripcion" class="form-label">Descripción</label>
                        <textarea class="form-control" id="descripcion" name="descripcion" rows="4"></textarea>
                    </div>

                    <div class="row">
                        <div class="col-md-4 mb-3">
                            <label for="disponible" class="form-label">Disponible</label>
                            <select class="form-select" id="disponible" name="disponible">
                                <option value="true" selected>Sí</option>
                                <option value="false">No</option>
                            </select>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="copias_disponibles" class="form-label">Copias Disponibles *</label>
                            <input type="number" class="form-control" id="copias_disponibles" name="copias_disponibles" value="1" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="calificacion" class="form-label">Calificación (1-5)</label>
                            <input type="number" class="form-control" id="calificacion" name="calificacion" min="1" max="5" step="0.1" value="4.0">
                        </div>
                    </div>

                    <div class="d-grid gap-2 mt-4">
                        <button type="submit" class="btn btn-success btn-lg">
                            💾 Guardar Libro en MongoDB
                        </button>
                        <a href="{% url 'listar_libros' %}" class="btn btn-secondary">Cancelar</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
    

📄 Template: biblioteca/templates/biblioteca/editar_libro.html

Ruta completa: biblioteca_project/biblioteca/templates/biblioteca/editar_libro.html

{% extends 'biblioteca/base.html' %}

{% block title %}Editar Libro{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-10">
        <div class="card shadow">
            <div class="card-header bg-warning text-dark">
                <h2 class="mb-0">✏️ Editar Libro en MongoDB Atlas</h2>
            </div>
            <div class="card-body">
                <form method="post">
                    {% csrf_token %}
                    
                    <div class="alert alert-info">
                        <strong>ID del Libro:</strong> {{ libro._id }}
                    </div>

                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="titulo" class="form-label">Título *</label>
                            <input type="text" class="form-control" id="titulo" name="titulo" value="{{ libro.titulo }}" required>
                        </div>

                        <div class="col-md-6 mb-3">
                            <label for="autor" class="form-label">Autor *</label>
                            <input type="text" class="form-control" id="autor" name="autor" value="{{ libro.autor }}" required>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-4 mb-3">
                            <label for="isbn" class="form-label">ISBN *</label>
                            <input type="text" class="form-control" id="isbn" name="isbn" value="{{ libro.isbn }}" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="anio_publicacion" class="form-label">Año de Publicación *</label>
                            <input type="number" class="form-control" id="anio_publicacion" name="anio_publicacion" value="{{ libro.anio_publicacion }}" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="categoria" class="form-label">Categoría *</label>
                            <input type="text" class="form-control" id="categoria" name="categoria" value="{{ libro.categoria }}" required>
                        </div>
                    </div>

                    <div class="mb-3">
                        <label for="descripcion" class="form-label">Descripción</label>
                        <textarea class="form-control" id="descripcion" name="descripcion" rows="4">{{ libro.descripcion }}</textarea>
                    </div>

                    <div class="row">
                        <div class="col-md-4 mb-3">
                            <label for="disponible" class="form-label">Disponible</label>
                            <select class="form-select" id="disponible" name="disponible">
                                <option value="true" {% if libro.disponible %}selected{% endif %}>Sí</option>
                                <option value="false" {% if not libro.disponible %}selected{% endif %}>No</option>
                            </select>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="copias_disponibles" class="form-label">Copias Disponibles *</label>
                            <input type="number" class="form-control" id="copias_disponibles" name="copias_disponibles" value="{{ libro.copias_disponibles }}" required>
                        </div>

                        <div class="col-md-4 mb-3">
                            <label for="calificacion" class="form-label">Calificación (1-5)</label>
                            <input type="number" class="form-control" id="calificacion" name="calificacion" min="1" max="5" step="0.1" value="{{ libro.calificacion }}">
                        </div>
                    </div>

                    <div class="d-grid gap-2 mt-4">
                        <button type="submit" class="btn btn-warning btn-lg">
                            💾 Actualizar Libro
                        </button>
                        <a href="{% url 'detalle_libro' libro._id %}" class="btn btn-secondary">Cancelar</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
    

📄 Template: biblioteca/templates/biblioteca/confirmar_eliminacion.html

Ruta completa: biblioteca_project/biblioteca/templates/biblioteca/confirmar_eliminacion.html

{% extends 'biblioteca/base.html' %}

{% block title %}Confirmar Eliminación{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card shadow border-danger">
            <div class="card-header bg-danger text-white">
                <h2 class="mb-0">⚠️ Confirmar Eliminación</h2>
            </div>
            <div class="card-body">
                <div class="alert alert-warning">
                    <h4>¿Estás seguro de eliminar este libro?</h4>
                    <hr>
                    <p><strong>Título:</strong> {{ libro.titulo }}</p>
                    <p><strong>Autor:</strong> {{ libro.autor }}</p>
                    <p><strong>ISBN:</strong> {{ libro.isbn }}</p>
                    <p class="text-danger mb-0"><strong>⚠️ Esta acción no se puede deshacer</strong></p>
                </div>

                <form method="post">
                    {% csrf_token %}
                    <div class="d-grid gap-2">
                        <button type="submit" class="btn btn-danger btn-lg">
                            🗑️ Sí, Eliminar Libro
                        </button>
                        <a href="{% url 'detalle_libro' libro._id %}" class="btn btn-secondary">
                            Cancelar
                        </a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
    

📄 Template: biblioteca/templates/biblioteca/estadisticas.html

Ruta completa: biblioteca_project/biblioteca/templates/biblioteca/estadisticas.html

{% extends 'biblioteca/base.html' %}

{% block title %}Estadísticas{% endblock %}

{% block extra_head %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock %}

{% block content %}
<div class="row">
    <div class="col-12">
        <h1 class="mb-4">📊 Estadísticas del Sistema Híbrido</h1>
    </div>
</div>

<!-- Tarjetas de Resumen -->
<div class="row mb-4">
    <div class="col-md-3">
        <div class="card text-white bg-primary shadow">
            <div class="card-body">
                <h5 class="card-title">📚 Total Libros</h5>
                <h2 class="display-4">{{ total_libros }}</h2>
                <p class="mb-0"><small>En MongoDB Atlas</small></p>
            </div>
        </div>
    </div>

    <div class="col-md-3">
        <div class="card text-white bg-success shadow">
            <div class="card-body">
                <h5 class="card-title">✅ Disponibles</h5>
                <h2 class="display-4">{{ libros_disponibles }}</h2>
                <p class="mb-0"><small>Listos para préstamo</small></p>
            </div>
        </div>
    </div>

    <div class="col-md-3">
        <div class="card text-white bg-warning shadow">
            <div class="card-body">
                <h5 class="card-title">📖 Préstamos</h5>
                <h2 class="display-4">{{ total_prestamos }}</h2>
                <p class="mb-0"><small>Registrados en MySQL</small></p>
            </div>
        </div>
    </div>

    <div class="col-md-3">
        <div class="card text-white bg-info shadow">
            <div class="card-body">
                <h5 class="card-title">👁️ Total Visitas</h5>
                <h2 class="display-4">{{ total_visitas }}</h2>
                <p class="mb-0"><small>En MongoDB Estadísticas</small></p>
            </div>
        </div>
    </div>
</div>

<!-- Gráficas -->
<div class="row">
    <div class="col-md-6 mb-4">
        <div class="card shadow">
            <div class="card-header bg-primary text-white">
                <h4 class="mb-0">📊 Libros Más Vistos</h4>
            </div>
            <div class="card-body">
                <canvas id="chartVistos"></canvas>
            </div>
        </div>
    </div>

    <div class="col-md-6 mb-4">
        <div class="card shadow">
            <div class="card-header bg-success text-white">
                <h4 class="mb-0">📖 Libros Más Prestados</h4>
            </div>
            <div class="card-body">
                <canvas id="chartPrestados"></canvas>
            </div>
        </div>
    </div>
</div>

<!-- Tabla de Libros Más Populares -->
<div class="row">
    <div class="col-12">
        <div class="card shadow">
            <div class="card-header bg-dark text-white">
                <h4 class="mb-0">🏆 Top 10 Libros Más Populares</h4>
            </div>
            <div class="card-body">
                <table class="table table-hover">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Título</th>
                            <th>Autor</th>
                            <th>Visitas</th>
                            <th>Préstamos</th>
                            <th>Calificación</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for libro in top_libros %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            <td><a href="{% url 'detalle_libro' libro._id %}">{{ libro.titulo }}</a></td>
                            <td>{{ libro.autor }}</td>
                            <td><span class="badge bg-info">{{ libro.views|default:0 }}</span></td>
                            <td><span class="badge bg-success">{{ libro.prestamos|default:0 }}</span></td>
                            <td>⭐ {{ libro.calificacion|default:"N/A" }}</td>
                        </tr>
                        {% empty %}
                        <tr>
                            <td colspan="6" class="text-center">No hay datos disponibles</td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
{% endblock %}

{% block extra_js %}
<script>
// Gráfica de Libros Más Vistos
const ctxVistos = document.getElementById('chartVistos').getContext('2d');
const chartVistos = new Chart(ctxVistos, {
    type: 'bar',
    data: {
        labels: [
            {% for libro in top_vistos %}
                '{{ libro.titulo|truncatechars:20 }}'{% if not forloop.last %},{% endif %}
            {% endfor %}
        ],
        datasets: [{
            label: 'Vistas',
            data: [
                {% for libro in top_vistos %}
                    {{ libro.views }}{% if not forloop.last %},{% endif %}
                {% endfor %}
            ],
            backgroundColor: 'rgba(240, 147, 251, 0.7)',
            borderColor: 'rgba(240, 147, 251, 1)',
            borderWidth: 2
        }]
    },
    options: {
        responsive: true,
        scales: {
            y: { beginAtZero: true }
        }
    }
});

// Gráfica de Libros Más Prestados
const ctxPrestados = document.getElementById('chartPrestados').getContext('2d');
const chartPrestados = new Chart(ctxPrestados, {
    type: 'bar',
    data: {
        labels: [
            {% for libro in top_prestados %}
                '{{ libro.titulo|truncatechars:20 }}'{% if not forloop.last %},{% endif %}
            {% endfor %}
        ],
        datasets: [{
            label: 'Préstamos',
            data: [
                {% for libro in top_prestados %}
                    {{ libro.prestamos }}{% if not forloop.last %},{% endif %}
                {% endfor %}
            ],
            backgroundColor: 'rgba(245, 87, 108, 0.7)',
            borderColor: 'rgba(245, 87, 108, 1)',
            borderWidth: 2
        }]
    },
    options: {
        responsive: true,
        scales: {
            y: { beginAtZero: true }
        }
    }
});
</script>
{% endblock %}
    

✅ Estructura de Carpetas Final del Sistema Híbrido

biblioteca_project/                          ← Raíz del proyecto
│
├── biblioteca_project/                      ← Configuración principal
│   ├── __init__.py
│   ├── settings.py                          ← MySQL + MongoDB configurados ⚙️
│   ├── urls.py                              ← Rutas principales (incluye admin + biblioteca)
│   ├── wsgi.py
│   └── asgi.py
│
├── biblioteca/                              ← Aplicación Django
│   ├── __init__.py
│   ├── models.py                            ← Modelos MySQL (Prestamo, Multa) 📦
│   ├── views.py                             ← 7 views híbridas (MySQL + MongoDB) 🔧
│   ├── urls.py                              ← Rutas de la app biblioteca 🔗
│   ├── admin.py                             ← Configuración del 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 + navbar) 🎨
│           ├── inicio.html                  ← Página de inicio con resumen
│           ├── dashboard.html               ← Estadísticas híbridas con gráficas 📊
│           ├── estadisticas.html            ← Dashboard avanzado con Chart.js 📈
│           ├── listar_libros.html           ← Catálogo completo (MongoDB Atlas) 📚
│           ├── lista_libros.html            ← Vista alternativa de libros
│           ├── detalle_libro.html           ← Detalle individual (MongoDB + MySQL) 📖
│           ├── crear_libro.html             ← Formulario crear libro (MongoDB) ➕
│           ├── editar_libro.html            ← Formulario editar libro (MongoDB) ✏️
│           ├── confirmar_eliminacion.html   ← Confirmar borrado (MongoDB) 🗑️
│           ├── mis_prestamos.html           ← Consulta híbrida ⭐ (libro_id común)
│           └── registrar_prestamo.html      ← Registrar préstamos (MySQL) 📝
│
├── init_mongodb.py                          ← Script para crear 4 BDs en Atlas 🚀
├── test_mongo.py                            ← Script de prueba de conexión ✅
├── manage.py                                ← Gestor de Django
├── db.sqlite3                               ← Base de datos MySQL local
├── requirements.txt                         ← Dependencias del proyecto
└── venv/                                    ← Entorno virtual Python
        

📌 Archivos Importantes que Debes Crear:

🎉 ¡Sistema Híbrido 100% Completo!

Ahora tienes TODOS los archivos necesarios:

🚀 Comandos para Ejecutar el Sistema Completo:

# 1. Activar entorno virtual
venv\Scripts\activate  # Windows
# source venv/bin/activate  # Linux/Mac

# 2. Instalar dependencias
pip install django pymongo dnspython mysqlclient

# 3. Verificar conexión a MongoDB Atlas
python test_mongo.py

# 4. Inicializar MongoDB Atlas con las 4 bases de datos
python init_mongodb.py

# 5. Migrar tablas MySQL
python manage.py makemigrations
python manage.py migrate

# 6. Crear superusuario para admin
python manage.py createsuperuser

# 7. Ejecutar servidor de desarrollo
python manage.py runserver

# 8. Abrir navegador en:
http://localhost:8000/              ← Página de inicio
http://localhost:8000/dashboard/    ← Dashboard con gráficas
http://localhost:8000/libros/       ← Catálogo MongoDB
http://localhost:8000/prestamos/    ← Mis préstamos (híbrido)
http://localhost:8000/admin/        ← Panel de administración

# Verás en acción:
# ✅ Dashboard con estadísticas de MySQL + MongoDB
# ✅ Catálogo de libros desde MongoDB Atlas (4 bases de datos)
# ✅ Sistema de préstamos que actualiza ambas BDs simultáneamente
# ✅ Vista "Mis Préstamos" que combina datos usando libro_id como campo común
# ✅ Gráficas interactivas con Chart.js