🔧 Views y URLs Completos

Todas las vistas y configuración de URLs del sistema

1. biblioteca_project/views.py - Vistas Principales

Ubicación: biblioteca_project/views.py
Descripción: Vistas para la página de inicio y ejemplos.

from django.shortcuts import render
from libros.models import Libro, Autor, Categoria, Prestamo
from django.contrib.auth.models import User

def home(request):
    """Vista de la página principal"""
    context = {
        'total_libros': Libro.objects.count(),
        'total_autores': Autor.objects.count(),
        'total_categorias': Categoria.objects.count(),
        'total_prestamos': Prestamo.objects.filter(estado='activo').count(),
    }
    return render(request, 'home.html', context)

def ejemplos_rest(request):
    """Vista de ejemplos API REST"""
    return render(request, 'ejemplos_rest.html')

def ejemplos_soap(request):
    """Vista de ejemplos SOAP"""
    return render(request, 'ejemplos_soap.html')

def ejemplos_admin(request):
    """Vista de ejemplos Panel Admin"""
    return render(request, 'ejemplos_admin.html')

2. libros/views.py - Vistas de la App Libros

Ubicación: libros/views.py
Descripción: ViewSets para API REST y vistas tradicionales.

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q, Count
from rest_framework import viewsets, filters
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend
from datetime import date, timedelta

from .models import Libro, Autor, Categoria, Editorial, Prestamo
from .serializers import (
    LibroSerializer, AutorSerializer, CategoriaSerializer,
    EditorialSerializer, PrestamoSerializer
)

# ========== VIEWSETS REST API ==========

class LibroViewSet(viewsets.ModelViewSet):
    """ViewSet para gestión de libros via API REST"""
    queryset = Libro.objects.all()
    serializer_class = LibroSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['categoria', 'autor', 'editorial', 'estado']
    search_fields = ['titulo', 'isbn', 'autor__nombre', 'autor__apellido']
    ordering_fields = ['titulo', 'fecha_publicacion', 'stock_disponible']
    ordering = ['titulo']

class AutorViewSet(viewsets.ModelViewSet):
    """ViewSet para gestión de autores"""
    queryset = Autor.objects.all()
    serializer_class = AutorSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['nombre', 'apellido', 'nacionalidad']
    ordering_fields = ['nombre', 'apellido']
    ordering = ['apellido']

class CategoriaViewSet(viewsets.ModelViewSet):
    """ViewSet para gestión de categorías"""
    queryset = Categoria.objects.all()
    serializer_class = CategoriaSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]

class EditorialViewSet(viewsets.ModelViewSet):
    """ViewSet para gestión de editoriales"""
    queryset = Editorial.objects.all()
    serializer_class = EditorialSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]

class PrestamoViewSet(viewsets.ModelViewSet):
    """ViewSet para gestión de préstamos"""
    queryset = Prestamo.objects.all()
    serializer_class = PrestamoSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ['estado', 'usuario', 'libro']
    ordering_fields = ['fecha_prestamo', 'fecha_devolucion_esperada']
    ordering = ['-fecha_prestamo']

# ========== VISTAS TRADICIONALES ==========

def index(request):
    """Página de inicio de la app libros"""
    context = {
        'total_libros': Libro.objects.count(),
        'total_autores': Autor.objects.count(),
        'prestamos_activos': Prestamo.objects.filter(estado='activo').count(),
        'total_usuarios': User.objects.count(),
    }
    return render(request, 'libros/index.html', context)

def catalogo(request):
    """Vista del catálogo de libros con filtros"""
    libros_list = Libro.objects.select_related('autor', 'categoria', 'editorial').all()
    
    # Aplicar filtros
    categoria_id = request.GET.get('categoria')
    autor_id = request.GET.get('autor')
    estado = request.GET.get('estado')
    
    if categoria_id:
        libros_list = libros_list.filter(categoria_id=categoria_id)
    if autor_id:
        libros_list = libros_list.filter(autor_id=autor_id)
    if estado == 'disponible':
        libros_list = libros_list.filter(stock_disponible__gt=0)
    elif estado == 'prestado':
        libros_list = libros_list.filter(stock_disponible=0)
    
    # Paginación
    paginator = Paginator(libros_list, 12)
    page_number = request.GET.get('page')
    libros = paginator.get_page(page_number)
    
    context = {
        'libros': libros,
        'total_libros': Libro.objects.count(),
        'categorias': Categoria.objects.all(),
        'autores': Autor.objects.all(),
    }
    return render(request, 'libros/catalogo.html', context)

def detalle_libro(request, libro_id):
    """Vista de detalle de un libro"""
    libro = get_object_or_404(
        Libro.objects.select_related('autor', 'categoria', 'editorial'),
        id=libro_id
    )
    context = {'libro': libro}
    return render(request, 'libros/detalle_libro.html', context)

def busqueda(request):
    """Vista de búsqueda de libros"""
    query = request.GET.get('q', '')
    resultados = []
    
    if query:
        resultados = Libro.objects.filter(
            Q(titulo__icontains=query) |
            Q(isbn__icontains=query) |
            Q(autor__nombre__icontains=query) |
            Q(autor__apellido__icontains=query)
        ).select_related('autor', 'categoria')[:50]
    
    context = {
        'query': query,
        'resultados': resultados,
        'categorias': Categoria.objects.all(),
        'autores': Autor.objects.all(),
    }
    return render(request, 'libros/busqueda.html', context)

def estadisticas(request):
    """Vista de estadísticas del sistema"""
    from django.contrib.auth.models import User
    import json
    from datetime import timedelta
    
    # Libros por categoría
    libros_por_categoria = Categoria.objects.annotate(
        total=Count('libro')
    ).order_by('-total')
    
    # Datos para gráfico de categorías (convertir a listas de Python)
    categorias_labels = list(libros_por_categoria.values_list('nombre', flat=True))
    categorias_data = list(libros_por_categoria.values_list('total', flat=True))
    
    # Préstamos del último mes (por día)
    hoy = date.today()
    hace_30_dias = hoy - timedelta(days=30)
    
    prestamos_labels = []
    prestamos_data = []
    for i in range(30):
        dia = hace_30_dias + timedelta(days=i)
        prestamos_del_dia = Prestamo.objects.filter(
            fecha_prestamo__date=dia
        ).count()
        prestamos_labels.append(dia.strftime('%d/%m'))
        prestamos_data.append(prestamos_del_dia)
    
    # Autores más prestados
    autores_prestados = Autor.objects.annotate(
        total_prestamos=Count('libro__prestamo')
    ).order_by('-total_prestamos')[:10]
    
    # Libros más populares
    libros_populares = Libro.objects.annotate(
        total_prestamos=Count('prestamo')
    ).order_by('-total_prestamos')[:10]
    
    context = {
        'total_libros': Libro.objects.count(),
        'prestamos_activos': Prestamo.objects.filter(estado='activo').count(),
        'total_usuarios': User.objects.count(),
        'libros_disponibles': Libro.objects.filter(stock_disponible__gt=0).count(),
        'libros_por_categoria': libros_por_categoria,
        # Convertir a JSON para JavaScript (usar json.dumps)
        'categorias_labels': json.dumps(categorias_labels),
        'categorias_data': json.dumps(categorias_data),
        'prestamos_labels': json.dumps(prestamos_labels),
        'prestamos_data': json.dumps(prestamos_data),
        'top_autores': autores_prestados,
        'top_libros': libros_populares,
    }
    return render(request, 'libros/estadisticas.html', context)

@login_required
def mi_cuenta(request):
    """Vista de perfil de usuario"""
    prestamos_activos = Prestamo.objects.filter(
        usuario=request.user,
        estado='activo'
    ).select_related('libro', 'libro__autor')
    
    historial_prestamos = Prestamo.objects.filter(
        usuario=request.user
    ).exclude(estado='activo').select_related('libro')[:20]
    
    context = {
        'prestamos_activos': prestamos_activos,
        'historial_prestamos': historial_prestamos,
    }
    return render(request, 'libros/mi_cuenta.html', context)

@login_required
def solicitar_prestamo(request, libro_id):
    """Vista para solicitar un préstamo"""
    libro = get_object_or_404(Libro, id=libro_id)
    
    if request.method == 'POST':
        dias = int(request.POST.get('dias', 14))
        fecha_devolucion = date.today() + timedelta(days=dias)
        
        if libro.stock_disponible > 0:
            prestamo = Prestamo.objects.create(
                libro=libro,
                usuario=request.user,
                fecha_devolucion_esperada=fecha_devolucion,
                estado='activo'
            )
            
            # Reducir stock
            libro.stock_disponible -= 1
            libro.save()
            
            return redirect('mi_cuenta')
    
    context = {'libro': libro}
    return render(request, 'libros/solicitar_prestamo.html', context)

@login_required
def renovar_prestamo(request, prestamo_id):
    """Vista para renovar un préstamo"""
    prestamo = get_object_or_404(Prestamo, id=prestamo_id, usuario=request.user)
    
    if prestamo.estado == 'activo':
        # Añadir 14 días más
        prestamo.fecha_devolucion_esperada += timedelta(days=14)
        prestamo.estado = 'renovado'
        prestamo.save()
    
    return redirect('mi_cuenta')

3. biblioteca_project/urls.py - URLs Principales

Ubicación: biblioteca_project/urls.py
Descripción: Configuración de URLs del proyecto.

from django.contrib import admin
from django.urls import path, include, re_path
from django.contrib.auth import views as auth_views
from libros.soap_services import django_soap_application
from . import views

urlpatterns = [
    # Página principal
    path('', views.home, name='home'),
    
    # Páginas de ejemplos
    path('ejemplos/rest/', views.ejemplos_rest, name='ejemplos_rest'),
    path('ejemplos/soap/', views.ejemplos_soap, name='ejemplos_soap'),
    path('ejemplos/admin/', views.ejemplos_admin, name='ejemplos_admin'),
    
    # Admin de Django
    path('admin/', admin.site.urls),
    
    # Servicio SOAP
    re_path(r'^soap/', django_soap_application),
    
    # API REST
    path('api/', include('libros.urls')),
    
    # Autenticación
    path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(next_page='/'), name='logout'),
]

4. libros/urls.py - URLs de la App Libros

Ubicación: libros/urls.py
Descripción: URLs para vistas y API REST de libros.

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

# Router para API REST
router = DefaultRouter()
router.register(r'libros', views.LibroViewSet)
router.register(r'autores', views.AutorViewSet)
router.register(r'categorias', views.CategoriaViewSet)
router.register(r'editoriales', views.EditorialViewSet)
router.register(r'prestamos', views.PrestamoViewSet)

urlpatterns = [
    # API REST
    path('', include(router.urls)),
    
    # Vistas tradicionales
    path('index/', views.index, name='index'),
    path('catalogo/', views.catalogo, name='catalogo'),
    path('libro//', views.detalle_libro, name='detalle_libro'),
    path('busqueda/', views.busqueda, name='busqueda'),
    path('estadisticas/', views.estadisticas, name='estadisticas'),
    path('mi-cuenta/', views.mi_cuenta, name='mi_cuenta'),
    path('solicitar-prestamo//', views.solicitar_prestamo, name='solicitar_prestamo'),
    path('renovar-prestamo//', views.renovar_prestamo, name='renovar_prestamo'),
]

5. biblioteca_project/settings.py - Configuración (Extracto)

Ubicación: biblioteca_project/settings.py
Descripción: Configuraciones clave del proyecto.

# INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Apps de terceros
    'rest_framework',
    'django_filters',
    'spyne',
    
    # Apps del proyecto
    'libros',
]

# REST FRAMEWORK
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
}

# BASE DE DATOS MYSQL
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'biblioteca_dbutres',
        'USER': 'root',
        'PASSWORD': '12345678',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
            'charset': 'utf8mb4',
        },
    }
}

# ZONA HORARIA
USE_TZ = False
TIME_ZONE = 'America/Hermosillo'

# TEMPLATES
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'biblioteca_project' / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# ARCHIVOS ESTÁTICOS
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

# CONFIGURACIÓN DE LOGIN
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

6. libros/serializers.py - Serializers REST

Ubicación: libros/serializers.py
Descripción: Serializers para la API REST.

from rest_framework import serializers
from .models import Libro, Autor, Categoria, Editorial, Prestamo

class AutorSerializer(serializers.ModelSerializer):
    """Serializer para el modelo Autor"""
    nombre_completo = serializers.CharField(read_only=True)
    
    class Meta:
        model = Autor
        fields = ['id', 'nombre', 'apellido', 'nombre_completo', 
                  'nacionalidad', 'biografia', 'fecha_nacimiento']

class CategoriaSerializer(serializers.ModelSerializer):
    """Serializer para el modelo Categoria"""
    class Meta:
        model = Categoria
        fields = ['id', 'nombre', 'descripcion']

class EditorialSerializer(serializers.ModelSerializer):
    """Serializer para el modelo Editorial"""
    class Meta:
        model = Editorial
        fields = ['id', 'nombre', 'pais', 'sitio_web', 'fecha_fundacion']

class LibroSerializer(serializers.ModelSerializer):
    """Serializer para el modelo Libro"""
    autor_nombre = serializers.CharField(source='autor.nombre_completo', read_only=True)
    categoria_nombre = serializers.CharField(source='categoria.nombre', read_only=True)
    editorial_nombre = serializers.CharField(source='editorial.nombre', read_only=True)
    
    class Meta:
        model = Libro
        fields = [
            'id', 'titulo', 'isbn', 'descripcion', 'fecha_publicacion',
            'numero_paginas', 'idioma', 'stock_total', 'stock_disponible',
            'estado', 'autor', 'autor_nombre', 'categoria', 'categoria_nombre',
            'editorial', 'editorial_nombre', 'fecha_creacion'
        ]
        read_only_fields = ['fecha_registro', 'ultima_actualizacion']

class PrestamoSerializer(serializers.ModelSerializer):
    """Serializer para el modelo Prestamo"""
    libro_titulo = serializers.CharField(source='libro.titulo', read_only=True)
    usuario_nombre = serializers.CharField(source='usuario.username', read_only=True)
    
    class Meta:
        model = Prestamo
        fields = [
            'id', 'libro', 'libro_titulo', 'usuario', 'usuario_nombre',
            'fecha_prestamo', 'fecha_devolucion_esperada', 'fecha_devolucion_real',
            'estado', 'renovaciones', 'multa', 'notas'
        ]
        read_only_fields = ['fecha_prestamo']
    
    def validate(self, data):
        """Validar que hay stock disponible"""
        libro = data.get('libro')
        if libro and libro.stock_disponible <= 0:
            raise serializers.ValidationError(
                "No hay ejemplares disponibles de este libro."
            )
        return data
    
    def create(self, validated_data):
        """Crear préstamo y actualizar stock"""
        prestamo = super().create(validated_data)
        libro = prestamo.libro
        libro.stock_disponible -= 1
        libro.save()
        return prestamo

7. libros/admin.py - Configuración Admin

Ubicación: libros/admin.py
Descripción: Configuración del panel administrativo de Django.

from django.contrib import admin
from .models import Autor, Categoria, Editorial, Libro, Prestamo

@admin.register(Autor)
class AutorAdmin(admin.ModelAdmin):
    list_display = ['nombre', 'apellido', 'nacionalidad', 'fecha_nacimiento']
    search_fields = ['nombre', 'apellido', 'nacionalidad']
    list_filter = ['nacionalidad']

@admin.register(Categoria)
class CategoriaAdmin(admin.ModelAdmin):
    list_display = ['nombre', 'descripcion']
    search_fields = ['nombre']

@admin.register(Editorial)
class EditorialAdmin(admin.ModelAdmin):
    list_display = ['nombre', 'pais', 'sitio_web']
    search_fields = ['nombre', 'pais']
    list_filter = ['pais']

@admin.register(Libro)
class LibroAdmin(admin.ModelAdmin):
    list_display = ['titulo', 'autor', 'categoria', 'stock_disponible', 'estado']
    search_fields = ['titulo', 'isbn', 'autor__nombre', 'autor__apellido']
    list_filter = ['categoria', 'editorial', 'estado', 'idioma']
    readonly_fields = ['fecha_registro']
    
    fieldsets = (
        ('Información Básica', {
            'fields': ('titulo', 'autor', 'isbn', 'descripcion')
        }),
        ('Detalles de Publicación', {
            'fields': ('editorial', 'fecha_publicacion', 'numero_paginas', 'idioma')
        }),
        ('Categorización', {
            'fields': ('categoria',)
        }),
        ('Stock', {
            'fields': ('stock_total', 'stock_disponible', 'estado')
        }),
        ('Metadatos', {
            'fields': ('fecha_creacion',),
            'classes': ('collapse',)
        }),
    )

@admin.register(Prestamo)
class PrestamoAdmin(admin.ModelAdmin):
    list_display = ['libro', 'usuario', 'fecha_prestamo', 
                    'fecha_devolucion_esperada', 'estado']
    search_fields = ['libro_titulo', 'usuario_username', 'usuario__email']
    list_filter = ['estado', 'fecha_prestamo']
    readonly_fields = ['fecha_prestamo']
    date_hierarchy = 'fecha_prestamo'
    
    fieldsets = (
        ('Información del Préstamo', {
            'fields': ('libro', 'usuario')
        }),
        ('Fechas', {
            'fields': ('fecha_prestamo', 'fecha_devolucion_esperada', 
                       'fecha_devolucion_real')
        }),
        ('Estado', {
            'fields': ('estado', 'observaciones')
        }),
    )

✅ Resumen de Views y URLs

Se han documentado 7 archivos completos:

1. biblioteca_project/views.py - 4 vistas principales
2. libros/views.py - 10 vistas + 5 ViewSets REST
3. biblioteca_project/urls.py - URLs del proyecto
4. libros/urls.py - URLs de la app + Router REST
5. settings.py - Configuración clave
6. serializers.py - 5 serializers REST
7. admin.py - Configuración del admin

Total de código:
- 15 vistas tradicionales
- 5 ViewSets REST
- 5 Serializers
- 4 Admin classes
- Router REST completo

Para implementar:
1. Copia cada archivo a su ubicación
2. Verifica las importaciones
3. Aplica las migraciones
4. Prueba cada endpoint

→ Continuar con CSS y JavaScript