En este capítulo vamos a darle vida a la página principal de nuestra tienda de videojuegos (home/index.html
), que hasta ahora no mostraba más que un mensaje estático. Nuestro objetivo será crear una portada visualmente atractiva que muestre juegos organizados por categorías, utilizando datos reales del modelo Juego
de la app juegos
.
Aunque estas categorías no existen aún como campo en el modelo, simularemos su existencia agrupando los juegos por IDs fijos. De este modo, podremos ver el resultado visual y preparar el terreno para una futura mejora del modelo.
Situación inicial: una plantilla vacía
La plantilla index.html
de la app home
solo contenía un encabezado con algo de texto. Este es el código original:
{% extends 'main.html' %} {% block content %} <section class="py-4 bg-body-secondary"> <div class="container"> <div class="row mt-4"> <div class="col-lg-8 mx-auto text-center mb-4"> <h3 class="text-body fw-bold"> Tu tienda de confianza con precios más altos, pero de confianza. </h3> </div> </div> </div> </section> {% endblock content %}
No había contenido dinámico, ni conexión con los modelos, ni tarjetas visuales.
Añadir categorías de prueba en la vista
Dado que el modelo Juego
no tiene aún una relación directa con categorías como “Más vendidos” o “Recomendados”, decidimos hacer una agrupación manual por IDs. Este método es provisional, pero suficiente para simular un sistema de categorías.
Así modificamos la vista index
en home/views.py
:
from django.shortcuts import render from juegos.models import Juego # Importamos el modelo desde otra app def index(request): juegos_por_categoria = { 'Más vendidos': Juego.objects.filter(id__in=[1, 2, 3, 8]), 'Recomendados': Juego.objects.filter(id__in=[6, 1, 7, 2]), 'Novedades': Juego.objects.filter(id__in=[4, 9, 10, 1]), } return render(request, 'home/index.html', { 'categorias': juegos_por_categoria })
Aquí usamos un diccionario para organizar los juegos en tres grupos. Luego pasamos este diccionario al contexto de la plantilla como
categorias
.
Mostrar las tarjetas en index.html
Una vez tenemos los datos listos, rediseñamos la plantilla para mostrar las tarjetas en filas, agrupadas por categoría. Cada juego aparece como una tarjeta con imagen, descripción y botón.
Este es el nuevo contenido de index.html
:
{% extends 'main.html' %} {% load static %} {% block content %} <section class="py-4 bg-body-secondary"> <div class="container"> <div class="row mt-4"> <div class="col-lg-8 mx-auto text-center mb-4"> <h3 class="text-body fw-bold"> Tu tienda de confianza con precios más altos, pero de confianza. </h3> </div> </div> {% for categoria, juegos in categorias.items %} <div class="mb-5"> <h4 class="mb-3">{{ categoria }}</h4> <div class="row"> {% for juego in juegos %} <div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-4 d-flex justify-content-center"> <a href="{% url 'juegos.mostrar' id=juego.id %}" class="card" style="width: 18rem; text-decoration: none; color: inherit;"> <img src="{% static 'img/portadas/' %}{{ juego.id }}.webp" class="card-img-top" alt="Imagen del juego {{ juego.nombre }}"> <div class="card-body d-flex flex-column text-center"> <p class="card-text flex-grow-1">{{ juego.descripcion|truncatewords:20 }}</p> <span class="btn btn-secondary mt-auto text-white">{{ juego.nombre }}</span> </div> </a> </div> {% endfor %} </div> </div> {% endfor %} </div> </section> {% endblock content %}
Con este código, por cada categoría se crea una fila (<div class="row">
) que contiene tarjetas en columnas. Gracias a Bootstrap, estas tarjetas se acomodan automáticamente según el tamaño de pantalla.
Aplicando un enlace al logo general de la cabecera
Cambiando de tema, gracias a tener todo estructurado en un proyecto dinámico, podemos implementar un enlace para el logo de la cabecera de todas las páginas del proyecto Django, con un solo cambio en el archivo base llamado main.html:
Cambia esto:
<img src="{% static 'img/logo.png' %}" alt="PF Gaming Logo" style="max-height: 200px" class="img-fluid logo-img" />
Por esto:
<a href="{% url 'home.index' %}"> <img src="{% static 'img/logo.png' %}" alt="PF Gaming Logo" style="max-height: 200px" class="img-fluid logo-img" /> </a>
Código final del bloque <header>
con el enlace ya integrado:
<header class="bg-body text-body py-0 border-bottom"> <div class="container"> <div class="row align-items-center"> <div class="col-md-4 text-center text-md-start mb-3 mb-md-0"> <a href="{% url 'home.index' %}"> <img src="{% static 'img/logo.png' %}" alt="PF Gaming Logo" style="max-height: 200px" class="img-fluid logo-img" /> </a> </div> <div class="col-md-8 text-center text-md-start"> <h1 class="display-5 fw-bold mb-1">PF Gaming</h1> <p class="lead mb-0">Tu universo gamer en un solo lugar</p> </div> </div> </div> </header>
Ahora, al hacer clic en el logo, los usuarios serán redirigidos a la vista de inicio (home.index
).
Añadiendo una tarjeta especial de “Ver todos los juegos”
En esta fase del proyecto, he decidido enriquecer visualmente la página de inicio añadiendo una tarjeta especial al final de cada categoría de juegos. Esta tarjeta sirve como llamada a la acción para que el usuario pueda explorar todo el catálogo de videojuegos disponible en el sitio.
¿Qué queremos lograr?
En cada sección de juegos (por ejemplo: “Más vendidos”, “Recomendados”, etc.), después de mostrar tres juegos, queremos que aparezca una cuarta tarjeta que:
- Muestra el logo del sitio como imagen.
- Contiene el título
"Ver todos los juegos"
. - Redirige al usuario a la página principal de la app
juegos
.
¿Por qué solo después del tercer juego?
Esta decisión es meramente visual y temporal. Como estamos trabajando con una base de datos simulada, limitamos cada sección a tres elementos para no saturar el diseño, y dejamos ese cuarto slot como CTA (Call to Action).
Cambios en la plantilla home/templates/home/index.html
Anteriormente, mostrábamos los juegos así:
{% for juego in juegos %} <div class="col-md-4 col-lg-3 mb-4 d-flex justify-content-center"> <a href="{% url 'juegos.mostrar' id=juego.id %}" class="card" style="width: 18rem; text-decoration: none; color: inherit;"> <img src="{% static 'img/portadas/' %}{{ juego.id }}.webp" class="card-img-top" alt="Imagen del juego {{ juego.nombre }}"> <div class="card-body d-flex flex-column text-center"> <p class="card-text flex-grow-1">{{ juego.descripcion|truncatewords:20 }}</p> <span class="btn btn-secondary mt-auto text-white">{{ juego.nombre }}</span> </div> </a> </div> {% endfor %}
Ahora, para limitar el bucle a los primeros 3 juegos y luego añadir la tarjeta extra, usamos esta estructura:
{% for juego in juegos %} {% if forloop.counter <= 3 %} <!-- Tarjeta normal del juego --> <div class="col-md-4 col-lg-3 mb-4 d-flex justify-content-center"> <a href="{% url 'juegos.mostrar' id=juego.id %}" class="card" style="width: 18rem; text-decoration: none; color: inherit;"> <img src="{% static 'img/portadas/' %}{{ juego.id }}.webp" class="card-img-top" alt="Imagen del juego {{ juego.nombre }}"> <div class="card-body d-flex flex-column text-center"> <p class="card-text flex-grow-1">{{ juego.descripcion|truncatewords:20 }}</p> <span class="btn btn-secondary mt-auto text-white">{{ juego.nombre }}</span> </div> </a> </div> {% endif %} {% endfor %} <!-- Tarjeta adicional con logo --> <div class="col-md-4 col-lg-3 mb-4 d-flex justify-content-center"> <a href="{% url 'juegos.index' %}" class="card" style="width: 18rem; text-decoration: none; color: inherit;"> <img src="{% static 'img/logo.png' %}" class="card-img-top p-3" style="max-height: 180px; object-fit: contain;" alt="Ver todos los juegos"> <div class="card-body d-flex flex-column text-center"> <p class="card-text flex-grow-1">Explora nuestro catálogo completo de videojuegos</p> <span class="btn btn-secondary mt-auto text-white">Ver todos los juegos</span> </div> </a> </div>
Cambios en la vista de home
Vamos a quitar uno de los cuatro id de cada fila en la vista, ya que solo necesitamos 3 de momento:
def index(request): juegos_por_categoria = { 'Más vendidos': Juego.objects.filter(id__in=[1, 2, 3]), 'Recomendados': Juego.objects.filter(id__in=[6, 1, 7]), 'Novedades': Juego.objects.filter(id__in=[4, 9, 10]), } return render(request, 'home/index.html', { 'categorias': juegos_por_categoria })
Mejora visual de los botones en la página de detalle del juego
Para mejorar la apariencia de los botones en la plantilla de detalle del juego (juegos/detalle.html
), he realizado los siguientes cambios:
Antes
Los botones estaban usando la clase btn-lg
, que los hacía muy grandes y con texto excesivamente grande, lo que restaba elegancia y usabilidad a la interfaz.
<!-- Botones de acción --> <div class="d-flex flex-column flex-sm-row gap-2"> <button class="btn btn-success btn-lg">Comprar ahora</button> <button class="btn btn-outline-secondary btn-lg">Agregar a favoritos</button> <a href="{% url 'juegos.index' %}" class="btn btn-outline-primary btn-lg">Volver al catálogo</a> </div>
Cambios aplicados
- Reducción del tamaño: Se cambió la clase
btn-lg
abtn-md
para un tamaño estándar más equilibrado. - Iconos visuales: Se añadieron iconos de Font Awesome para reforzar el significado de cada botón y mejorar la experiencia del usuario.
- Alineación vertical: Se añadió la clase
d-flex align-items-center
para alinear verticalmente el icono con el texto dentro del botón.
Después
<!-- Botones de acción --> <div class="d-flex flex-column flex-sm-row gap-2"> <button class="btn btn-success btn-md d-flex align-items-center"> <i class="fas fa-shopping-cart me-2"></i>Comprar ahora </button> <button class="btn btn-outline-secondary btn-md d-flex align-items-center"> <i class="fas fa-heart me-2"></i>Agregar a favoritos </button> <a href="{% url 'juegos.index' %}" class="btn btn-outline-primary btn-md d-flex align-items-center"> <i class="fas fa-arrow-left me-2"></i>Volver al catálogo </a> </div>
Resultado visual
- Botones con tamaño cómodo y proporcionado.
- Iconos que complementan el texto y aportan claridad.
- Mejor alineación que evita que el icono o texto se desplacen verticalmente.