APLICAWEB 4to/Propuesta Intensiva 8 Semanas/Anexos/Banco de Live Coding · Unidad 2
20 retos · Unidad 2 · ≤ 3 min
20

Banco de 20 Retos de Live Coding — Unidad 2

Los 20 retos parten de tu propio proyecto Django tal como queda al terminar la práctica de la Semana 4: el modelo Servicio en MySQL (Aiven), admin.py, las vistas inicio y servicios (ya con Servicio.objects.all()), las plantillas inicio.html y servicios.html, settings.py con WhiteNoise y el Web Service en Render. Cada reto muestra el código ANTES y DESPUÉS y cómo se ve en el navegador.

Cómo se evalúa: el docente elige uno al azar. Todos son cambios chicos sobre código que ya escribiste y se resuelven en 3 minutos o menos: se evalúa que encuentres el archivo correcto, cambies solo lo necesario y expliques por qué mientras lo haces. «Se evalúa» lista lo que se revisa y «En tu práctica» te dice dónde está.
Tip: las líneas citadas corresponden a miproyecto-django_pdb del grupo 4-1; si tu proyecto tiene otro nombre (ej. miproyecto/settings.py), busca el mismo bloque. Cada reto trae un bloque «Cómo resolverlo en VS Code» con los pasos en orden; los comandos se escriben en la terminal integrada de VS Code, sin el $.

Antes de cualquier reto: tu terminal es la de VS Code

  1. Abre la carpeta del proyecto: Archivo → Abrir carpeta… y elige la que tiene manage.py (ej. miproyecto-django_pdb).
  2. Abre la terminal integrada: menú Ver → Terminal o Ctrl + ñ (teclado en español; en inglés es Ctrl + `). Se abre abajo, ya ubicada en la carpeta del proyecto, con PowerShell.
  3. Revisa que diga (venv) al inicio de la línea. Si no aparece, actívalo con el primer comando de abajo.
  4. Abre archivos rápido con Ctrl + P y escribe el nombre (ej. models.py). Guarda siempre con Ctrl + S antes de correr un comando.
  5. Dos terminales: runserver ocupa la terminal. Para correr otro comando sin detenerlo, toca el + del panel de la terminal; para detenerlo, Ctrl + C.
TERMINAL DE VS CODE — PowerShell
# activa el entorno virtual (Windows, PowerShell de VS Code)
$ venv\Scripts\Activate.ps1
# si PowerShell bloquea el script, corre esto una vez y repite la línea anterior
$ Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# levanta el servidor local para ver tus cambios en http://127.0.0.1:8000/
$ python manage.py runserver
Unidad 2

Django + MySQL en la nube + Render (Semanas 3-4)

01

Agregar el campo disponible al modelo y migrar en vivooficial · lista de cotejo

⏱ 3 minSemana 4core/models.py + terminal de VS Code

Consigna del docente: “Agrega a Servicio el campo disponible (sí/no, por defecto sí), mígralo y muéstrame que tus registros siguen ahí.”

ANTES — core/models.py
    # categoria: el último campo de texto que ya tenías
    categoria = models.CharField(max_length=60)
    # creado_en: la fecha que Django llena solo al crear el registro
    creado_en = models.DateTimeField(auto_now_add=True)
DESPUÉS — core/models.py
    # categoria: no se toca
    categoria = models.CharField(max_length=60)
    # creado_en: no se toca
    creado_en = models.DateTimeField(auto_now_add=True)
    # disponible: campo NUEVO; BooleanField = columna sí/no (tinyint en MySQL)
    # default=True: a los 5 registros que ya existen se les pone True, por eso no se rompen
    disponible = models.BooleanField(default=True)

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe models.py → Enter.
  2. Debajo de creado_en escribe la línea de disponible, con la misma sangría (4 espacios).
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. Abre la terminal de VS Code (Ctrl + ñ) y confirma que dice (venv). Si ahí corre runserver, detenlo con Ctrl + C o abre otra terminal con el +.
  5. Corre makemigrations y después migrate (bloque de abajo). En el Explorador de VS Code aparece core/migrations/0002_servicio_disponible.py.
  6. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/admin/ en el navegador. Entra a un servicio y muestra la casilla Disponible.
TERMINAL DE VS CODE — PowerShell
# genera el plan de cambios: core/migrations/0002_servicio_disponible.py
$ python manage.py makemigrations core
# aplica el plan: agrega la columna disponible en MySQL (Aiven)
$ python manage.py migrate
# levanta el servidor para comprobarlo en /admin
$ python manage.py runserver

Vista — formulario «Change servicio» en /admin

127.0.0.1:8000/admin/core/servicio/1/change/
Django administration

Titulo: Tutorías de programación

Precio: 150.00

Categoria: Tutorías

127.0.0.1:8000/admin/core/servicio/1/change/
Django administration

Titulo: Tutorías de programación

Precio: 150.00

Categoria: Tutorías

☑ Disponible

Los 5 registros siguen ahí, todos con ☑ ✓

SE EVALÚA
  • Escribe el campo dentro de la clase Servicio, con la misma sangría que los demás
  • Explica por qué default=True evita que Django pregunte qué valor darle a las filas viejas
  • Corre los dos comandos en orden y reconoce el archivo 0002_servicio_disponible.py nuevo

📍 En tu práctica: core/models.py, justo debajo de creado_en (línea 25) y antes de def __str__.

02

Hacer que __str__ muestre título y precio

⏱ 2 minSemana 4core/models.py

Consigna del docente: “En /admin cada servicio aparece solo con su título. Haz que también se vea el precio, sin tocar el admin.”

ANTES — core/models.py
    # __str__: el texto con el que Django "presenta" cada objeto
    def __str__(self):
        # hoy devuelve solo el título
        return self.titulo
DESPUÉS — core/models.py
    # __str__: mismo método, no cambia el nombre
    def __str__(self):
        # f-string: arma un texto con el título, un guion y el precio del objeto
        return f"{self.titulo} — ${self.precio}"

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe models.py → Enter.
  2. Baja al final de la clase y cambia solo la línea del return.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/admin/core/servicio/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — lista de Servicios en /admin

127.0.0.1:8000/admin/core/servicio/
Django administration

Select servicio to change

Tutorías de programación
Diseño de logotipos
Repostería por encargo
127.0.0.1:8000/admin/core/servicio/
Django administration

Select servicio to change

Tutorías de programación — $150.00
Diseño de logotipos — $300.00
Repostería por encargo — $120.00
SE EVALÚA
  • Reconoce que el texto de la lista del admin sale de __str__, no del admin.py
  • Usa self. para leer los campos del propio objeto
  • Sabe que este cambio no necesita migración: no cambia ninguna columna

📍 En tu práctica: core/models.py, líneas 28-30 (el final de la clase).

03

Ordenar la tabla de servicios del más nuevo al más viejooficial · lista de cotejo

⏱ 2 minSemana 4core/views.py

Consigna del docente: “La vista servicios() trae los registros sin orden. Muestra primero el último que cargaste y dime qué SQL arma Django.”

ANTES — core/views.py
    # all(): trae todas las filas, en el orden que las devuelva MySQL
    lista_servicios = Servicio.objects.all()
DESPUÉS — core/views.py
    # order_by('-creado_en'): el guion significa descendente (más nuevo primero)
    # Django lo traduce a: SELECT ... FROM core_servicio ORDER BY creado_en DESC
    lista_servicios = Servicio.objects.order_by('-creado_en')

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe views.py → Enter.
  2. Dentro de def servicios, cambia .all() por .order_by('-creado_en').
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — /servicios/

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00
Repostería por encargo$120.00
tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Repostería por encargo$120.00
Diseño de logotipos$300.00
Tutorías de programación$150.00

El último cargado queda arriba ✓

SE EVALÚA
  • Cambia solo la consulta, no la plantilla
  • Explica qué hace el - delante del nombre del campo
  • Nombra el ORDER BY ... DESC que Django genera por detrás

📍 En tu práctica: core/views.py, dentro de def servicios(request), la línea de lista_servicios.

04

Agregar la columna «Categoría» a la tabla pública

⏱ 3 minSemana 3-4core/templates/core/servicios.html

Consigna del docente: “Tu modelo tiene categoria pero la tabla no la muestra. Agrega esa columna.”

ANTES — servicios.html
<!-- encabezado: hoy solo dos columnas -->
<th class="text-left px-4 py-2">Servicio</th>
<!-- segunda columna del encabezado -->
<th class="text-left px-4 py-2">Precio</th>
<!-- ...dentro del for, las dos celdas de cada fila -->
<td class="px-4 py-2">{{ servicio.titulo }}</td>
<!-- celda del precio -->
<td class="px-4 py-2">${{ servicio.precio }}</td>
DESPUÉS — servicios.html
<!-- encabezado existente, no se toca -->
<th class="text-left px-4 py-2">Servicio</th>
<!-- th NUEVO: mismas clases que los otros encabezados -->
<th class="text-left px-4 py-2">Categoría</th>
<!-- encabezado existente, no se toca -->
<th class="text-left px-4 py-2">Precio</th>
<!-- celda existente del título -->
<td class="px-4 py-2">{{ servicio.titulo }}</td>
<!-- td NUEVO: lee el campo categoria del objeto actual del for -->
<td class="px-4 py-2">{{ servicio.categoria }}</td>
<!-- celda existente del precio -->
<td class="px-4 py-2">${{ servicio.precio }}</td>

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe servicios.html → Enter.
  2. Agrega el <th> en el <thead> y el <td> dentro del {% for %}, en la misma posición.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — /servicios/

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00
Repostería por encargo$120.00
tu-proyecto.onrender.com/servicios/

Servicios

ServicioCategoríaPrecio
Tutorías de programaciónTutorías$150.00
Diseño de logotiposDiseño$300.00
Repostería por encargoRepostería$120.00
SE EVALÚA
  • Agrega el <th> y el <td> en la misma posición para que no se desfasen las columnas
  • Escribe el nombre del campo tal cual está en models.py (categoria, sin tilde)
  • No toca la vista: el dato ya venía en cada objeto

📍 En tu práctica: servicios.html, <thead> en las líneas 13-18 y las celdas dentro del {% for %} en las líneas 24-26.

05

Mostrar cuántos servicios hay con el filtro |length

⏱ 2 minSemana 3core/templates/core/servicios.html

Consigna del docente: “Debajo del título escribe cuántos servicios hay en total, sin tocar views.py.”

ANTES — servicios.html
<!-- título de la página -->
<h1 class="text-2xl font-bold mb-6">Servicios</h1>
<!-- tabla: empieza justo después del h1 -->
<table class="w-full border border-slate-200 rounded-lg overflow-hidden">
DESPUÉS — servicios.html
<!-- h1: mb-6 baja a mb-2 para que el contador quede pegado al título -->
<h1 class="text-2xl font-bold mb-2">Servicios</h1>
<!-- p NUEVO: |length cuenta cuántos elementos trae la variable servicios -->
<p class="text-sm text-slate-500 mb-6">Total: {{ servicios|length }} servicios</p>
<!-- tabla: no se toca -->
<table class="w-full border border-slate-200 rounded-lg overflow-hidden">

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe servicios.html → Enter.
  2. Debajo del <h1> agrega el <p> con {{ servicios|length }}.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — /servicios/

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00
Repostería por encargo$120.00
tu-proyecto.onrender.com/servicios/

Servicios

Total: 3 servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00
Repostería por encargo$120.00
SE EVALÚA
  • Usa {{ }} (mostrar un valor) y no {% %}
  • Explica qué es un filtro: lo que va después de la barra | transforma el valor
  • Usa la misma variable servicios que recorre el for

📍 En tu práctica: servicios.html, línea 10 (el <h1>).

06

Mostrar un mensaje cuando no hay servicios con {% empty %}

⏱ 3 minSemana 3core/templates/core/servicios.html

Consigna del docente: “Si la tabla de MySQL queda vacía, hoy se ve un encabezado solo. Muestra una fila que diga que todavía no hay servicios.”

ANTES — servicios.html
<!-- cierra la fila que se repite en cada vuelta -->
</tr>
<!-- endfor: cierra el bucle; si no hay datos no se dibuja nada -->
{% endfor %}
DESPUÉS — servicios.html
<!-- cierra la fila que se repite, no se toca -->
</tr>
<!-- empty NUEVO: lo que sigue se muestra solo si la lista está vacía -->
{% empty %}
<!-- fila de aviso; colspan="2" la estira sobre las dos columnas -->
<tr><td colspan="2" class="px-4 py-6 text-center text-slate-500">Todavía no hay servicios.</td></tr>
<!-- endfor: sigue cerrando el bucle, ahora después del empty -->
{% endfor %}

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe servicios.html → Enter.
  2. Justo antes de {% endfor %} agrega {% empty %} y la fila de aviso.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Para verlo sin borrar datos, prueba un momento con Servicio.objects.none() en la vista (lista vacía) y después vuelve a .all().

Vista — /servicios/ con la tabla vacía

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio

Solo el encabezado: ¿se rompió o no hay datos?

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Todavía no hay servicios.
SE EVALÚA
  • Pone {% empty %} dentro del for, antes de {% endfor %}
  • Usa colspan igual al número de columnas
  • Lo prueba: borra (o desmarca) registros en /admin o explica cuándo aparecería

📍 En tu práctica: servicios.html, línea 29 ({% endfor %}).

07

Filtrar la vista para mostrar una sola categoría

⏱ 2 minSemana 4core/views.py

Consigna del docente: “Haz que /servicios/ muestre solo los servicios de la categoría Tutorías.”

ANTES — core/views.py
    # all(): todas las filas de la tabla
    lista_servicios = Servicio.objects.all()
DESPUÉS — core/views.py
    # filter(categoria='Tutorías'): solo las filas cuya categoría sea ese texto
    # Django lo traduce a: SELECT ... WHERE categoria = 'Tutorías'
    lista_servicios = Servicio.objects.filter(categoria='Tutorías')

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe views.py → Enter.
  2. Cambia .all() por .filter(categoria='Tutorías') (con el texto exacto que cargaste en /admin).
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — /servicios/

tu-proyecto.onrender.com/servicios/

Servicios

ServicioCategoría
Tutorías de programaciónTutorías
Diseño de logotiposDiseño
Repostería por encargoRepostería
Asesoría en ExcelTutorías
tu-proyecto.onrender.com/servicios/

Servicios

ServicioCategoría
Tutorías de programaciónTutorías
Asesoría en ExcelTutorías
SE EVALÚA
  • Cambia all() por filter(...) y escribe el campo sin comillas y el valor con comillas
  • El texto debe coincidir con el que cargó en /admin
  • Nombra el WHERE que genera el ORM

📍 En tu práctica: core/views.py, la línea de lista_servicios dentro de servicios().

08

Mostrar columnas en la lista del admin con list_display

⏱ 3 minSemana 4core/admin.py

Consigna del docente: “En /admin solo se ve el título. Haz que la lista muestre título, categoría, precio y dueño en columnas.”

ANTES — core/admin.py
# admin: el módulo del panel /admin
from django.contrib import admin
# Servicio: el modelo de esta misma app
from .models import Servicio

# register: registro simple, sin personalizar nada
admin.site.register(Servicio)
DESPUÉS — core/admin.py
# admin: no se toca
from django.contrib import admin
# Servicio: no se toca
from .models import Servicio

# @admin.register: REEMPLAZA a admin.site.register (no pueden estar los dos)
@admin.register(Servicio)
# ServicioAdmin: clase que personaliza cómo se ve Servicio en el panel
class ServicioAdmin(admin.ModelAdmin):
    # list_display: los campos que aparecen como columnas en la lista
    list_display = ('titulo', 'categoria', 'precio', 'propietario')

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe admin.py → Enter.
  2. Borra la línea admin.site.register(Servicio) y escribe en su lugar el decorador y la clase ServicioAdmin.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. Mira la terminal de VS Code donde corre runserver: si hay un error (ej. AlreadyRegistered), aparece ahí.
  5. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/admin/core/servicio/ en el navegador.

Vista — /admin/core/servicio/

tu-proyecto.onrender.com/admin/core/servicio/
Django administration

Select servicio to change

Tutorías de programación
Diseño de logotipos
tu-proyecto.onrender.com/admin/core/servicio/
Django administration

Select servicio to change

TituloCategoriaPrecioPropietario
Tutorías de prog.Tutorías150.00admin
Diseño de logotiposDiseño300.00admin
SE EVALÚA
  • Borra la línea admin.site.register(Servicio) (si queda, Django tira AlreadyRegistered)
  • Escribe los nombres de campo exactamente como en models.py
  • Sabe que no hace falta migrar: solo cambia el panel, no la tabla

📍 En tu práctica: core/admin.py, línea 7.

09

Cambiar la dirección /servicios/ por /catalogo/

⏱ 2 minSemana 3core/urls.py

Consigna del docente: “Quiero que la página de servicios se abra en /catalogo/. Cambia solo lo necesario.”

ANTES — core/urls.py
    # ruta 'servicios/' → función views.servicios → alias 'servicios'
    path('servicios/', views.servicios, name='servicios'),
DESPUÉS — core/urls.py
    # solo cambia el primer texto (lo que se escribe en el navegador)
    # la vista y el name='servicios' se quedan iguales
    path('catalogo/', views.servicios, name='servicios'),

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe urls.py → Enter. Elige el de core (en la lista se ve la carpeta a la derecha del nombre).
  2. Cambia solo el texto 'servicios/' por 'catalogo/'.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/catalogo/ en el navegador. Prueba también /servicios/ para mostrar el 404.

Vista — barra de direcciones

tu-proyecto.onrender.com/catalogo/

Page not found (404)

/catalogo/ todavía no existe; la tabla vive en /servicios/

tu-proyecto.onrender.com/catalogo/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00

Misma vista, nueva dirección ✓

SE EVALÚA
  • Distingue las tres partes de path(): dirección, función y alias
  • Cambia solo la primera, no la función ni el name
  • Explica que /servicios/ ahora da 404

📍 En tu práctica: core/urls.py, línea 13.

10

Mostrar el nombre de la base de datos en «Hola desde MySQL»

⏱ 3 minSemana 3core/views.py + inicio.html

Consigna del docente: “Tu página de inicio muestra motor y host. Agrega también el nombre de la base de datos.”

ANTES — views.py + inicio.html
    # contexto: los datos que la plantilla puede usar
    contexto = {
        # motor: el ENGINE de la conexión
        'motor': info_bd['ENGINE'],
        # host: el servidor de Aiven
        'host': info_bd['HOST'],
    # cierra el diccionario
    }
DESPUÉS — views.py + inicio.html
    # contexto: mismo diccionario, con una clave más
    contexto = {
        # motor: no se toca
        'motor': info_bd['ENGINE'],
        # host: no se toca
        'host': info_bd['HOST'],
        # base: clave NUEVA, lee el NAME de la conexión (defaultdb en Aiven)
        'base': info_bd['NAME'],
    # cierra el diccionario
    }
# --- y en core/templates/core/inicio.html, debajo del párrafo del host ---
# <p>Base de datos: {{ base }}</p>   ← {{ base }} = la clave nueva del contexto

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe views.py → Enter. Agrega la clave 'base' al diccionario contexto.
  2. Con Ctrl + P abre inicio.html y agrega <p>Base de datos: {{ base }}</p> debajo del host.
  3. Guarda los dos archivos (Ctrl + K y luego S guarda todos).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/ en el navegador.

Vista — página de inicio

127.0.0.1:8000/

Hola desde MySQL

Motor de base de datos: django.db.backends.mysql

Host: mysql-xxxx.aivencloud.com

127.0.0.1:8000/

Hola desde MySQL

Motor de base de datos: django.db.backends.mysql

Host: mysql-xxxx.aivencloud.com

Base de datos: defaultdb

SE EVALÚA
  • Agrega la clave en la vista y la usa en la plantilla con el mismo nombre
  • Explica el viaje: diccionario de la vista → {{ variable }} del template
  • No olvida la coma al final de la línea anterior del diccionario

📍 En tu práctica: core/views.py líneas 11-14 (el contexto) y core/templates/core/inicio.html línea 13.

11

Agregar la fecha de alta con el filtro |date

⏱ 3 minSemana 4core/templates/core/servicios.html

Consigna del docente: “Muestra en la tabla la fecha en que se cargó cada servicio, en formato día/mes/año.”

ANTES — servicios.html
<!-- última celda de cada fila -->
<td class="px-4 py-2">${{ servicio.precio }}</td>
DESPUÉS — servicios.html
<!-- celda del precio, no se toca -->
<td class="px-4 py-2">${{ servicio.precio }}</td>
<!-- td NUEVO: creado_en con el filtro date; "d/m/Y" = 24/09/2026 -->
<td class="px-4 py-2">{{ servicio.creado_en|date:"d/m/Y" }}</td>
<!-- (y en el thead, un <th class="text-left px-4 py-2">Alta</th> al final) -->

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe servicios.html → Enter.
  2. Agrega el <th>Alta</th> al final del <thead> y el <td> con |date:"d/m/Y" al final de la fila.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — /servicios/

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00
tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecioAlta
Tutorías de programación$150.0022/09/2026
Diseño de logotipos$300.0023/09/2026
SE EVALÚA
  • Reconoce que creado_en lo llenó Django solo (auto_now_add)
  • Usa el filtro con dos puntos y el formato entre comillas
  • Agrega también el <th> para no desfasar la tabla

📍 En tu práctica: servicios.html, línea 26, y el <thead> en las líneas 14-17.

12

Enlazar la página de inicio con la de servicios usando {% url %}

⏱ 2 minSemana 3core/templates/core/inicio.html

Consigna del docente: “Desde «Hola desde MySQL» no hay forma de llegar a servicios. Agrega un enlace sin escribir la dirección a mano.”

ANTES — inicio.html
  <!-- último párrafo de la página -->
  <p>Host: {{ host }}</p>
<!-- cierra el body -->
</body>
DESPUÉS — inicio.html
  <!-- párrafo del host, no se toca -->
  <p>Host: {{ host }}</p>
  <!-- a NUEVO: {% url 'servicios' %} busca la ruta por su name= en urls.py -->
  <a href="{% url 'servicios' %}">Ver servicios →</a>
<!-- cierra el body -->
</body>

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe inicio.html → Enter.
  2. Debajo del párrafo del host escribe el <a> con {% url 'servicios' %}.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/ en el navegador. Haz clic en el enlace.

Vista — página de inicio

127.0.0.1:8000/

Hola desde MySQL

Host: mysql-xxxx.aivencloud.com

Sin enlaces a otras páginas

127.0.0.1:8000/

Hola desde MySQL

Host: mysql-xxxx.aivencloud.com

Ver servicios →

El href generado es /servicios/ (o /catalogo/ si hiciste el reto 09) ✓

SE EVALÚA
  • Usa el name= de la ruta, no la dirección escrita a mano
  • Explica la ventaja: si cambia la dirección (reto 09) el enlace sigue funcionando
  • Pone comillas simples adentro porque el href ya usa dobles

📍 En tu práctica: core/templates/core/inicio.html, después de la línea 13; el name='servicios' está en core/urls.py línea 13.

13

Hacer que la descripción sea opcional con blank=True

⏱ 3 minSemana 4core/models.py + terminal de VS Code

Consigna del docente: “En /admin no me deja guardar un servicio sin descripción. Haz que sea opcional.”

ANTES — core/models.py
    # descripcion: texto largo; hoy es OBLIGATORIO en los formularios
    descripcion = models.TextField()
DESPUÉS — core/models.py
    # blank=True: el formulario (admin) acepta dejarlo vacío
    descripcion = models.TextField(blank=True)

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe models.py → Enter.
  2. En la línea de descripcion escribe blank=True dentro de los paréntesis.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. Abre la terminal de VS Code (Ctrl + ñ) y confirma que dice (venv).
  5. Corre makemigrations y migrate (bloque de abajo). En el Explorador aparece el archivo ..._alter_servicio_descripcion.py.
  6. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/admin/core/servicio/add/ en el navegador. Guarda un servicio sin descripción.
TERMINAL DE VS CODE — PowerShell
# genera el plan: core/migrations/000X_alter_servicio_descripcion.py
$ python manage.py makemigrations core
# aplica el cambio en MySQL (Aiven)
$ python manage.py migrate
# levanta el servidor para probarlo en /admin
$ python manage.py runserver

Vista — «Add servicio» en /admin con la descripción vacía

127.0.0.1:8000/admin/core/servicio/add/
Django administration

Please correct the error below.

Descripcion:

This field is required.

127.0.0.1:8000/admin/core/servicio/add/
Django administration

The servicio “Asesoría en Excel” was added successfully.

Descripcion: (no está en negrita = opcional)

SE EVALÚA
  • Pone blank=True dentro de los paréntesis del campo correcto
  • Sabe que en el admin, etiqueta en negrita = campo obligatorio
  • Corre makemigrations + migrate y explica por qué Django genera una migración aunque no cambie el tipo de columna

📍 En tu práctica: core/models.py, línea 16.

14

Poner el proyecto en español de México y hora de Hermosillo

⏱ 2 minSemana 3settings.py

Consigna del docente: “Tu /admin está en inglés y la hora de creación sale en UTC. Arregla las dos cosas en settings.py.”

ANTES — miproyecto_pdb/settings.py
# LANGUAGE_CODE: idioma de los textos de Django (admin, errores)
LANGUAGE_CODE = 'en-us'
# TIME_ZONE: zona horaria con la que se muestran las fechas
TIME_ZONE = 'UTC'
DESPUÉS — miproyecto_pdb/settings.py
# 'es-mx': español de México; el admin se traduce solo
LANGUAGE_CODE = 'es-mx'
# 'America/Hermosillo': hora de Sonora (UTC-7, sin horario de verano)
TIME_ZONE = 'America/Hermosillo'

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe settings.py → Enter.
  2. Busca con Ctrl + F LANGUAGE_CODE y cambia los dos valores.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/admin/ en el navegador.
  5. Si el servidor ya estaba corriendo, solo recarga el navegador (F5): Django detecta el cambio solo.

Vista — portada de /admin

127.0.0.1:8000/admin/
Django administration

Site administration

Servicios · + Add · Change

127.0.0.1:8000/admin/
Administración de Django

Sitio administrativo

Servicios · + Añadir · Modificar

SE EVALÚA
  • Ubica las dos variables en la parte «Internationalization» de settings.py
  • Escribe los valores como texto entre comillas
  • Explica que con USE_TZ = True MySQL guarda en UTC y Django convierte a la hora local al mostrar

📍 En tu práctica: miproyecto_pdb/settings.py, líneas 131 y 133.

15

Corregir la posición de WhiteNoiseMiddleware

⏱ 2 minSemana 4settings.py

Consigna del docente: “Revisa tu MIDDLEWARE: WhiteNoise no está donde dice su documentación. Muévelo.”

ANTES — miproyecto_pdb/settings.py
MIDDLEWARE = [
    # 1.º: cabeceras de seguridad
    'django.middleware.security.SecurityMiddleware',
    # 2.º: sesiones
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 3.º: WhiteNoise quedó DESPUÉS de sesiones (mal ubicado)
    'whitenoise.middleware.WhiteNoiseMiddleware',
DESPUÉS — miproyecto_pdb/settings.py
MIDDLEWARE = [
    # 1.º: cabeceras de seguridad, sigue primero
    'django.middleware.security.SecurityMiddleware',
    # 2.º: WhiteNoise JUSTO después de SecurityMiddleware, como pide su documentación
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # 3.º: sesiones baja un lugar; el resto de la lista no se toca
    'django.contrib.sessions.middleware.SessionMiddleware',

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe settings.py → Enter.
  2. Busca MIDDLEWARE con Ctrl + F.
  3. Pon el cursor en la línea de WhiteNoise y súbela con Alt + ↑ hasta que quede debajo de SecurityMiddleware (mueve la línea entera, sin cortar ni pegar).
  4. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  5. En la terminal de VS Code corre python manage.py check (bloque de abajo) para confirmar que no hay errores.
TERMINAL DE VS CODE — PowerShell
# revisa la configuración del proyecto sin levantar el servidor
$ python manage.py check

Vista — orden en que pasa cada petición

MIDDLEWARE (arriba → abajo)
  1. Security
  2. Session
  3. WhiteNoise ✗
  4. Common…

Un .css estático pasa por sesiones sin necesidad

MIDDLEWARE (arriba → abajo)
  1. Security
  2. WhiteNoise ✓
  3. Session
  4. Common…

Los estáticos se responden antes, sin tocar sesiones

SE EVALÚA
  • Reconoce que el orden de la lista importa: cada petición la recorre de arriba abajo
  • Mueve la línea completa sin borrar ninguna coma
  • Cita la regla: «justo después de SecurityMiddleware»

📍 En tu práctica: miproyecto_pdb/settings.py, líneas 51-61. En tu práctica WhiteNoise está en la línea 55, debajo de SessionMiddleware.

16

Dejar de tener DEBUG = True fijo en el código

⏱ 3 minSemana 4settings.py + .env

Consigna del docente: “Tu DEBUG está en True escrito a mano. Haz que se lea del .env y que por defecto sea False.”

ANTES — miproyecto_pdb/settings.py
# DEBUG fijo: en Render también quedaría encendido
DEBUG = True
DESPUÉS — settings.py + .env
# config('DEBUG'): lee la variable del .env (local) o de Render (producción)
# default=False: si la variable no existe, se apaga (opción segura)
# cast=bool: convierte el texto "True"/"False" en booleano de Python
DEBUG = config('DEBUG', default=False, cast=bool)

# --- en tu .env local agrega esta línea ---
# DEBUG=True

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe settings.py → Enter.
  2. Cambia DEBUG = True por la línea con config(...).
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. Con Ctrl + P abre .env, agrega DEBUG=True al final y guarda.
  5. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/xyz/ en el navegador. En local sigue la página amarilla (DEBUG=True); en Render, con DEBUG=False, sale el error genérico.

Vista — una URL que no existe en Render (/xyz/)

tu-proyecto.onrender.com/xyz/

Page not found (404)

Using the URLconf defined in miproyecto_pdb.urls, Django tried these URL patterns: admin/ · servicios/ …

Le muestra tus rutas a cualquiera ✗

tu-proyecto.onrender.com/xyz/

Not Found

The requested resource was not found on this server.

Error genérico, sin detalles internos ✓

SE EVALÚA
  • Explica por qué default=False es la opción segura
  • Sabe para qué sirve cast=bool (del .env todo llega como texto)
  • Agrega DEBUG=True en su .env local y DEBUG=False en Render

📍 En tu práctica: miproyecto_pdb/settings.py, línea 33. config ya está importado en la línea 20.

17

Diagnosticar y corregir ALLOWED_HOSTS vacíooficial · lista de cotejo

⏱ 3 minSemana 4settings.py + Render

Consigna del docente: “Con ALLOWED_HOSTS = [] y DEBUG=False en Render, ¿qué error sale? Corrígelo.”

ANTES — miproyecto_pdb/settings.py
# config: hoy solo se importa config
from decouple import config
# lista vacía: con DEBUG=False Django no acepta atender a NINGÚN dominio
ALLOWED_HOSTS = []
DESPUÉS — settings.py + Render
# Csv: convierte "a.com,b.com" en la lista ['a.com', 'b.com']
from decouple import config, Csv
# ALLOWED_HOSTS: se lee de la variable de entorno y se separa por comas
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='', cast=Csv())

# --- en Render → Environment ---
# ALLOWED_HOSTS=tu-proyecto.onrender.com
# --- en tu .env local ---
# ALLOWED_HOSTS=127.0.0.1,localhost

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe settings.py → Enter.
  2. En el import de decouple agrega , Csv y cambia la línea de ALLOWED_HOSTS.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. Con Ctrl + P abre .env y agrega ALLOWED_HOSTS=127.0.0.1,localhost.
  5. En la terminal de VS Code (con (venv)): python manage.py runserver y abre http://127.0.0.1:8000/servicios/ en el navegador. En Render, pestaña Environment, la variable lleva el dominio .onrender.com.

Vista — la URL de Render

tu-proyecto.onrender.com/servicios/

Bad Request (400)

Django rechaza el dominio porque no está en la lista ✗

tu-proyecto.onrender.com/servicios/

Servicios

ServicioPrecio
Tutorías de programación$150.00
Diseño de logotipos$300.00

El dominio de Render ya está permitido ✓

SE EVALÚA
  • Predice el error Bad Request (400) antes de probarlo
  • Agrega Csv al import que ya existía, no escribe otro import
  • Pone el dominio sin https:// ni barra final

📍 En tu práctica: miproyecto_pdb/settings.py, línea 35 (y el import de la línea 20).

18

Completar requirements.txt con gunicorn y whitenoise

⏱ 2 minSemana 4requirements.txt + terminal de VS Code

Consigna del docente: “Mira tu requirements.txt: ¿Render podría arrancar tu app con eso? Arréglalo.”

ANTES — requirements.txt
# asgiref: dependencia interna que Django instala solo
asgiref==3.12.1
# Django: el framework
Django==6.1.1
# PyMySQL: el conector a MySQL de Aiven
PyMySQL==1.2.0
# python-decouple: lee el .env con config()
python-decouple==3.8
# sqlparse: otra dependencia interna de Django
sqlparse==0.6.0
# tzdata: zonas horarias; y aquí termina: NO están gunicorn ni whitenoise
tzdata==2026.4
DESPUÉS — terminal de VS Code
# instala los dos paquetes de producción dentro del venv (si no estaban)
$ pip install gunicorn whitenoise
# pip freeze: lista lo instalado; > sobrescribe requirements.txt con esa lista
$ pip freeze > requirements.txt
# git diff: muestra las líneas nuevas (gunicorn==..., whitenoise==...)
$ git diff requirements.txt

Cómo resolverlo en VS Code

  1. Con Ctrl + P abre requirements.txt y muestra que faltan gunicorn y whitenoise.
  2. Abre la terminal de VS Code (Ctrl + ñ) y confirma que dice (venv).
  3. Corre los tres comandos del bloque «DESPUÉS», en orden.
  4. Mira requirements.txt en el editor: VS Code lo recarga solo con las líneas nuevas. También se ve en Control de código fuente (Ctrl + Shift + G) → clic en el archivo → vista de diferencias.

Vista — log de deploy en Render

dashboard.render.com → Logs

==> Running 'gunicorn miproyecto_pdb.wsgi'

bash: gunicorn: command not found

==> Exited with status 127

dashboard.render.com → Logs

Successfully installed gunicorn whitenoise

[INFO] Booting worker with pid: 52

==> Your service is live 🎉

SE EVALÚA
  • Detecta que faltan gunicorn y whitenoise leyendo el archivo
  • Explica que Render solo instala lo que dice requirements.txt (Build Command)
  • Usa pip freeze > con el venv activo, no lo escribe a mano

📍 En tu práctica: requirements.txt en la raíz del proyecto (junto a manage.py). En tu práctica tiene 6 líneas y ninguna es gunicorn.

19

Sacar db.sqlite3 del repositorio con .gitignore

⏱ 2 minSemana 3.gitignore + terminal de VS Code

Consigna del docente: “En tu carpeta quedó un db.sqlite3 de cuando Django usaba SQLite. Que Git lo ignore.”

ANTES — .gitignore
# venv/: no se sube el entorno virtual
venv/
# .env: contraseñas reales, nunca a GitHub
.env
# __pycache__/: archivos compilados de Python
__pycache__/
DESPUÉS — .gitignore
# las tres líneas de antes no se tocan
venv/
# .env sigue ignorado
.env
# __pycache__/ sigue ignorado
__pycache__/
# db.sqlite3: NUEVA; la base local vieja no es parte del código (usamos MySQL)
db.sqlite3
# staticfiles/: NUEVA; carpeta que collectstatic genera sola en Render
staticfiles/

Cómo resolverlo en VS Code

  1. En VS Code: Ctrl + P → escribe .gitignore → Enter.
  2. Al final agrega db.sqlite3 y staticfiles/, cada uno en su línea.
  3. Guarda con Ctrl + S (el punto blanco de la pestaña desaparece).
  4. En la terminal de VS Code corre git status (bloque de abajo). En el Explorador, db.sqlite3 se ve en gris: Git lo ignora.
TERMINAL DE VS CODE — PowerShell
# muestra qué archivos ve Git; db.sqlite3 ya no debe aparecer
$ git status

Vista — salida de git status

Terminal de VS Code

$ git status

Untracked files:

db.sqlite3

Terminal de VS Code

$ git status

Changes not staged for commit:

modified: .gitignore

(db.sqlite3 ya no aparece)

SE EVALÚA
  • Explica que db.sqlite3 sobra porque la app ahora usa MySQL en Aiven
  • Escribe cada nombre en su propia línea
  • Verifica el resultado con git status

📍 En tu práctica: .gitignore en la raíz. En tu práctica db.sqlite3 está junto a manage.py.

20

Publicar el cambio del reto 01 y comprobar el migrate en el log de Render

⏱ 3 minSemana 4terminal de VS Code + Render (Events y Logs)

Consigna del docente: “Ya agregaste disponible en local. Súbelo, muéstrame el redeploy y dónde se ve en el log de Render lo que hizo migrate.” En el plan Free de Render no hay pestaña Shell: migrate corre solo, dentro del Build Command (guía de la Semana 4, paso 06.2).

ANTES — terminal de VS Code
# muestra lo modificado: models.py y la migración nueva sin commit
$ git status
DESPUÉS — terminal de VS Code (Render hace el resto)
# prepara models.py y core/migrations/0002_servicio_disponible.py
$ git add .
# guarda el cambio con un mensaje que dice QUÉ se hizo
$ git commit -m "Agregar campo disponible a Servicio"
# sube a GitHub; Render detecta el push, redespliega y corre su Build Command
$ git push
# --- el Build Command de Render ya incluye migrate (no se escribe a mano): ---
# pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate

Cómo resolverlo en VS Code

  1. Abre la terminal de VS Code (Ctrl + ñ) en la carpeta del proyecto.
  2. Corre git status y muestra los dos archivos del reto 01.
  3. Corre git add ., git commit -m "..." y git push ahí mismo (o con los botones Commit y Sync de Ctrl + Shift + G).
  4. En el navegador: dashboard de Render → tu servicio → Events: aparece el deploy nuevo con el mensaje de tu commit.
  5. Abre la pestaña Logs de ese deploy y busca la parte del Build Command: ahí está lo que respondió migrate. Espera a que diga Your service is live.
  6. Si el deploy falla, la primera línea con Error del log dice por qué (por ejemplo, olvidaste subir el archivo 0002_...).

Vista — log del deploy en Render

Terminal de VS Code

$ git status

modified: core/models.py

Untracked: core/migrations/0002_servicio_disponible.py

dashboard.render.com → Logs

==> Running build command 'pip install … && python manage.py migrate'

Operations to perform: Apply all migrations: admin, auth, contenttypes, core, sessions

Running migrations:

No migrations to apply.

==> Your service is live 🎉

Sale así porque ya migraste en local contra el MISMO MySQL de Aiven

SE EVALÚA
  • Incluye la migración 0002_... en el commit (sin ella, Render no conoce el cambio)
  • Muestra en «Events» que el push disparó un deploy
  • Encuentra en «Logs» la salida de migrate y sabe que corre porque está en el Build Command (en el plan Free no hay Shell)
  • Explica por qué dice «No migrations to apply»: la tabla django_migrations de Aiven ya la tiene registrada porque migró en local contra la misma base. Si no hubiera migrado en local, el log diría Applying core.0002_servicio_disponible... OK

📍 En tu práctica: terminal integrada de VS Code, abierta en la carpeta del proyecto, y en Render: tu Web Service → pestañas Events y Logs. El Build Command está en Settings.

↑ Números