Saltar al contenido
Inicio 100 Días de Código en Python — Día 54: Tu primer servidor web con Flask (y los decoradores que lo hacen posible)

100 Días de Código en Python — Día 54: Tu primer servidor web con Flask (y los decoradores que lo hacen posible)

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 54 (nivel intermedio+).

Este tutorial adapta pedagógicamente el Día 54, que abre el bloque de desarrollo web con Flask (Días 54 a 58). Las secciones 📝 Contenido original del curso provienen de las transcripciones de las lecciones 388 a 393. El ejercicio del decorador que mide el tiempo se menciona al final de la lección 393, pero su enunciado no está entre los archivos suministrados; aquí se reconstruye a partir de lo que dice el video y se marca como tal. Los retos 🟢🟡🟠🔴, el proyecto extra y el proyecto integrador del bloque son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.

Nota sobre las verificaciones: el 27 de septiembre de 2026, con Python 3.14, Flask 3.1.3 y Werkzeug 3.1.8, se ejecutaron todos los ejemplos de código de este tutorial. En particular se comprobó el comando flask --app hello routes, el valor de __name__ en distintos casos y el mensaje de error que aparece al llamar requests.py a un archivo propio.


Tabla de contenidos

  1. Antes de empezar: frontend, backend y un restaurante
  2. Tu primer servidor con Flask
  3. La línea de comandos
  4. __name__ y __main__
  5. Las funciones son objetos de primera clase
  6. Decoradores y la sintaxis @
  7. 🚀 Proyecto del día: el servidor del salón
  8. 🌟 Proyecto extra: la caja de herramientas de decoradores
  9. 🏗️ Proyecto integrador del bloque Flask — Etapa 1
  10. 🧩 Comprueba lo aprendido
  11. 🤖 Programar con apoyo de IA
  12. 🛠️ Correcciones técnicas al material original
  13. 📋 Matriz de trazabilidad
  14. 📦 Evidencias de aprendizaje del Día 54

0. Antes de empezar: frontend, backend y un restaurante

(Fuente: 388 - Understanding Backend Web Development with Python.md)

🎯 Objetivo de aprendizaje

Explicar con tus palabras qué hacen el cliente, el servidor y la base de datos, y en qué se diferencia un sitio web de una aplicación web.

📝 Contenido original del curso

En los Días 41 a 44 hiciste sitios con HTML y CSS: eso es el frontend, lo que el usuario ve. Una aplicación web necesita además un backend: la parte que hace los cálculos, aplica las reglas del negocio y maneja los datos. Full stack significa dominar ambos.

  • Frontend: HTML (estructura), CSS (estilo) y JavaScript (interactividad). Frameworks populares: Angular, React.
  • Backend: puede escribirse en JavaScript, Java, Python, Ruby… Frameworks de Python: Flask, Django, Bottle, CherryPy, Pyramid. Los más pedidos en ofertas de empleo son Flask (mejor para principiantes y proyectos pequeños) y Django (proyectos comerciales grandes).

Los tres componentes del backend:

Componente Qué es En el restaurante
Cliente El navegador del usuario El comedor, donde se sienta el cliente
Servidor Un computador conectado a Internet, siempre encendido, que responde solicitudes La cocina, donde se preparan los platos
Base de datos Una "hoja de cálculo con esteroides" donde se guarda la información La despensa con los ingredientes

Cuando escribes google.com y presionas Enter, el navegador envía una solicitud (request) al servidor, que responde con archivos HTML, CSS y JavaScript. Si la página necesita datos (tus boletas en Eventbrite, tus listas de Spotify), el servidor los saca de la base de datos, los pone dentro del HTML y envía el "plato terminado". El cliente nunca entra a la cocina ni a la despensa.

📖 Ya conoces el otro lado (adaptación)

Desde el Día 33 has sido cliente muchas veces: requests.get(url) envía una solicitud y recibe una respuesta con un código de estado (200, 404…). Hoy cambias de lado: vas a escribir el programa que recibe esas solicitudes y decide qué responder.

Texto
Días 33–53:  tu programa ──request──▶ servidor ajeno ──response──▶ tu programa
Día 54+:     navegador   ──request──▶ TU servidor   ──response──▶ navegador

1. Tu primer servidor con Flask

(Fuente: 389 - Create your First Web Server with Flask.md)

🎯 Objetivo de aprendizaje

Crear, ejecutar y detener un servidor Flask mínimo, y distinguir un framework de una biblioteca.

📝 Contenido original del curso

Biblioteca contra framework. Con una biblioteca (como requests o Beautiful Soup), tú la llamas cuando la necesitas: requests.get(...). Con un framework, él te llama a ti: tú escribes funciones con la forma que el framework exige y él decide cuándo ejecutarlas. Solo tienes que planear: "si pasa esta situación, ¿qué código debe correr?".

Lo primero es la documentación. El Quickstart de Flask muestra una aplicación mínima. La profesora crea un proyecto hello_flask con un archivo hello.py:

Python
from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

Cuidado con el nombre del archivo. Si llamas a tu archivo igual que una biblioteca (por ejemplo requests.py o flask.py), el import carga tu archivo en lugar de la biblioteca y aparece un error confuso como "requests no tiene el atributo get".

Instalar Flask: con la bombilla roja de PyCharm, desde la configuración del intérprete, o en la terminal con pip install Flask. Cualquier paquete de pypi.org se instala con pip.

Ejecutarlo: ejecutar hello.py "normalmente" termina con código de salida 0 y no pasa nada: definir la aplicación no la pone a funcionar. Según el Quickstart de esa época, hay que indicar a Flask qué archivo contiene la aplicación con una variable de entorno y luego ejecutar flask run:

Terminal
# Mac
export FLASK_APP=hello.py
flask run
Batch
:: Windows (símbolo del sistema)
set FLASK_APP=hello.py
flask run

Aparece una dirección como http://127.0.0.1:5000: es tu sitio, servido por tu propio computador. Flask advierte que es un servidor de desarrollo y que no debe usarse en producción. En las herramientas de desarrollo de Chrome se ve que Flask envolvió tu texto en un documento HTML completo. Para detener el servidor: Ctrl + C. Si no lo detienes e intentas ejecutar otra aplicación en la misma dirección, la segunda no arrancará.

🛠️ Nota técnica (Flask 3.1, verificado): hoy la forma recomendada por la documentación es indicar la aplicación en el mismo comando, sin variables de entorno:

Terminal
flask --app hello run            # funciona igual en Windows, Mac y Linux
flask --app hello run --debug    # con recarga automática y depurador (Día 55)

Además, si el archivo se llama app.py o wsgi.py, basta con flask run. Y si usas PowerShell (la terminal por defecto de VS Code en Windows), set FLASK_APP=... no funciona; la sintaxis es $env:FLASK_APP = "hello.py".

🛠️ Nota técnica (Python 3.14, verificado): las versiones recientes de Python ya ayudan con el error del nombre del archivo:

Texto
AttributeError: module 'requests' has no attribute 'get' (consider renaming '...\requests.py'
if it has the same name as a library you intended to import)

Si ves "consider renaming", ya sabes qué pasó. Borra también la carpeta __pycache__ que se haya creado junto a tu archivo.

📖 Qué significa 127.0.0.1:5000 (adaptación)

Parte Significado
http:// El protocolo (sin cifrado, suficiente para desarrollo)
127.0.0.1 "Este mismo computador" (también se escribe localhost). Nadie más en la red puede entrar
:5000 El puerto: el "número de ventanilla" donde Flask atiende. Otro programa puede usar otro puerto
/ La ruta (route). @app.route("/") le dice a Flask qué función atiende esa ruta

⚠️ Errores frecuentes

Error Causa Solución
ModuleNotFoundError: No module named 'flask' Flask no está instalado en el intérprete que usas Instálalo en el entorno virtual del proyecto (pip install flask con el entorno activo)
Error: Could not locate a Flask application flask run no sabe qué archivo usar flask --app hello run, o nombra el archivo app.py
set no hace nada en VS Code La terminal es PowerShell, no cmd $env:FLASK_APP = "hello.py" o, mejor, --app
"Address already in use" o el puerto 5000 ocupado Otro servidor sigue corriendo Detén el anterior con Ctrl + C, o usa flask --app hello run --port 5001
El navegador muestra la versión vieja Cambiaste el código sin reiniciar el servidor Reinicia, o usa --debug (Día 55)

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1 — Aplicación: cambia el mensaje de la ruta / por un saludo al salón y agrega una ruta /bye que diga "¡Hasta luego!".
  • 🟡 Reto 2 — Ampliación: agrega tres rutas más (/horario, /materias, /contacto) y comprueba cuáles existen con flask --app hello routes.
  • 🟠 Reto 3 — Desafío: visita una ruta que no existe. ¿Qué código de estado devuelve? Compruébalo con requests.get("http://127.0.0.1:5000/no-existe").status_code desde otro programa mientras el servidor corre.
  • 🔴 Reto 4 — Pensamiento computacional: dibuja el recorrido completo de una solicitud a /horario: desde que presionas Enter en el navegador hasta que ves el texto. Marca qué parte hace el navegador, cuál hace Flask y cuál hace tu función.

2. La línea de comandos

(Fuente: 390 - Understand the Command Line on Windows and Mac.md)

🎯 Objetivo de aprendizaje

Moverte por las carpetas y crear, ver y borrar archivos desde la terminal, de forma segura.

📝 Contenido original del curso

Kernel y shell. Si el sistema operativo fuera un pistacho, la semilla sería el kernel (el programa que habla con el hardware) y la cáscara sería el shell (la interfaz para que los humanos hablen con el kernel). Hay shells gráficos (el Explorador de archivos, el Finder) y de línea de comandos. ¿Por qué usar la línea de comandos? Más control y, con práctica, más rapidez.

En Mac, la aplicación es Terminal (con zsh desde macOS Catalina); en Windows, el Símbolo del sistema. La terminal de PyCharm es una de esas mismas terminales dentro del editor.

Comando Qué hace
pwd Print working directory: muestra en qué carpeta estás
ls Lista los archivos y carpetas de la carpeta actual
cd Desktop Change directory: entra a una carpeta (tecla Tab para autocompletar)
cd .. Sube a la carpeta padre
mkdir Prueba Crea una carpeta
touch main.py Crea un archivo vacío (cambia la extensión para otros tipos)
rm main.py Borra un archivo
rm -rf Prueba Borra una carpeta y todo su contenido, recursivamente y sin preguntar

La profesora insiste: rm -rf no pide confirmación ni manda nada a la papelera. "Un gran poder conlleva una gran responsabilidad": verifica en qué carpeta estás y qué vas a borrar. Para aprender más comandos, busca una "cheat sheet" de la terminal o del símbolo del sistema.

🛠️ Nota técnica: el video afirma que "en su mayor parte" los mismos comandos sirven en Windows y en Mac. Eso es cierto en PowerShell (que tiene alias como ls, pwd y rm), pero no en el Símbolo del sistema (cmd), donde pwd, ls, touch y rm no existen. Equivalencias:

Acción Mac / Linux (zsh, bash) Windows — Símbolo del sistema (cmd) Windows — PowerShell
¿Dónde estoy? pwd cd (sin argumentos) pwd
Listar ls dir ls o dir
Entrar / subir cd carpeta / cd .. igual igual
Crear carpeta mkdir Prueba igual igual
Crear archivo vacío touch main.py type nul > main.py New-Item main.py
Borrar archivo rm main.py del main.py rm main.py
Borrar carpeta completa rm -rf Prueba rmdir /s /q Prueba Remove-Item -Recurse -Force Prueba

🏫 Para el aula (adaptación)

  • Trabaja siempre dentro de una carpeta de práctica (por ejemplo Documentos\python_decimo). Nunca ejecutes un borrado recursivo desde C:\ ni desde tu carpeta de usuario.
  • Antes de borrar, ejecuta ls (o dir) y lee lo que vas a eliminar.
  • Rutas con espacios van entre comillas: cd "Mis proyectos".

⚠️ Errores frecuentes

Error Causa Solución
'touch' no se reconoce como un comando interno o externo Estás en cmd Usa la tabla de equivalencias o PowerShell
El sistema no puede encontrar la ruta especificada Escribiste mal el nombre o no estás donde crees pwd/cd para ver dónde estás; usa Tab
No puedes borrar una carpeta Estás dentro de ella cd .. primero
cd Mis proyectos falla El espacio separa argumentos Comillas: cd "Mis proyectos"

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: solo con la terminal, crea sitio_salon/templates y sitio_salon/static (las necesitarás el Día 56) y un archivo sitio_salon/server.py.
  • 🟡 Reto 2: muévete entre esas carpetas usando solo rutas relativas (.., nombres) y luego una absoluta. ¿Cuál es más cómoda y cuál es más segura?
  • 🟠 Reto 3: crea tres archivos de prueba, bórralos uno por uno y luego borra la carpeta que los contenía. Antes de cada borrado, ejecuta el comando que lista el contenido.
  • 🔴 Reto 4: escribe una "cheat sheet" de 10 comandos para tu sistema operativo y tu terminal, con un ejemplo probado de cada uno.

3. __name__ y __main__

(Fuente: 391 - __name__ and __main__ - Special Attributes built into Python.md)

🎯 Objetivo de aprendizaje

Explicar qué contiene __name__ y para qué sirve if __name__ == "__main__":.

📝 Contenido original del curso

Flask(__name__) recibe un único dato obligatorio, el nombre de importación. Al imprimir __name__ en el archivo que se está ejecutando, aparece __main__: significa que el código se ejecuta como programa principal, no como un módulo importado por otro. En cambio, random.__name__ es "random".

Esto permite una forma común de arrancar Flask:

Python
if __name__ == "__main__":
    app.run()

app.run() hace lo mismo que flask run, pero sin variable de entorno y usando los botones normales de Ejecutar y Detener del editor.

📖 Explicación conceptual

__name__ es una variable que Python crea automáticamente en cada módulo (cada archivo .py):

Cómo se usa el archivo hello.py Valor de __name__ dentro de hello.py (verificado)
python hello.py o el botón Ejecutar "__main__"
Otro archivo hace import hello "hello"
flask --app hello run "hello" (Flask lo importa)

Por eso el bloque if __name__ == "__main__": es un "solo si me ejecutan directamente". Con flask run, ese bloque no se ejecuta, y no pasa nada malo: Flask arranca el servidor por su cuenta. Así el mismo archivo sirve de las dos formas.

🛠️ Nota técnica: la transcripción dice que __name__ sirve para averiguar "el nombre de la clase, función, método o descriptor actual". Funciones y clases tienen su propio atributo __name__ (print.__name__ es "print"), pero la variable __name__ que usas suelta en un archivo es el nombre del módulo. Es la que recibe Flask(__name__) para saber dónde buscar las carpetas templates y static (Día 56).

⚠️ Errores frecuentes

Error Causa Solución
if __name__ == "main": nunca se cumple Faltan los guiones bajos "__main__", con dos guiones bajos a cada lado
if __name__ == __main__: → NameError Faltan las comillas Es un texto: "__main__"
Un módulo auxiliar arranca el servidor al importarlo Tiene app.run() sin el if Pon app.run() dentro del if

4. Las funciones son objetos de primera clase

(Fuente: 392 - Python Functions as First Class Objects- Passing & Nesting Functions.md)

🎯 Objetivo de aprendizaje

Pasar funciones como argumentos, definir funciones anidadas y devolver una función desde otra.

📝 Contenido original del curso

La línea @app.route("/") es un decorador. Para entenderlo, primero hay que saber tres cosas sobre las funciones de Python. La profesora usa cuatro funciones de calculadora:

Python
def add(n1, n2):
    return n1 + n2

def subtract(n1, n2):
    return n1 - n2

def multiply(n1, n2):
    return n1 * n2

def divide(n1, n2):
    return n1 / n2

1. Se pueden pasar como argumento (son "objetos de primera clase", igual que un entero o un texto):

Python
def calculate(calc_function, n1, n2):
    return calc_function(n1, n2)

result = calculate(multiply, 2, 3)
print(result)   # 6   (con add sería 5)

2. Se pueden anidar: una función definida dentro de otra solo existe dentro de ella. Llamarla desde fuera da NameError.

Python
def outer_function():
    print("I'm outer")

    def nested_function():
        print("I'm inner")

    nested_function()

outer_function()   # I'm outer / I'm inner

3. Se pueden devolver sin ejecutarlas (sin paréntesis), guardarlas en una variable y llamarlas después:

Python
def outer_function():
    print("I'm outer")

    def nested_function():
        print("I'm inner")

    return nested_function

inner_function = outer_function()   # imprime "I'm outer"
inner_function()                    # imprime "I'm inner"

La profesora recomienda recorrer este código paso a paso en Python Tutor, que dibuja cómo la variable inner_function termina apuntando a nested_function.

📖 La clave: con paréntesis o sin paréntesis (adaptación)

Escribes Significa Ejemplo
multiply La función (la receta) calculate(multiply, 2, 3)
multiply(2, 3) El resultado de ejecutarla (el plato) 6

Ya lo usaste sin notarlo: en Turtle, screen.onkey(key="Up", fun=snake.up) (Día 20) y en Tkinter, Button(command=calcular) (Día 27). Pasabas la función sin paréntesis para que otro la llamara después. Eso es exactamente lo que hace Flask con tus rutas.

⚠️ Errores frecuentes

Error Causa Solución
TypeError: 'int' object is not callable Pasaste multiply(2, 3) (un número) donde se esperaba la función Pasa multiply, sin paréntesis
NameError: name 'nested_function' is not defined La llamaste fuera de la función que la contiene Llámala dentro, o devuélvela
return nested_function() Devuelves el resultado (aquí None), no la función return nested_function

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: agrega una función power(n1, n2) y úsala con calculate.
  • 🟡 Reto 2: crea un diccionario operaciones = {"+": add, "-": subtract, ...} y úsalo para calcular a partir de un símbolo que escribe el usuario (como la calculadora del Día 10).
  • 🟠 Reto 3: escribe aplicar_a_todos(funcion, lista) que devuelva una lista con funcion(x) para cada elemento. Pruébala con abs, str y una función propia.
  • 🔴 Reto 4: escribe crear_multiplicador(n) que devuelva una función que multiplica por n. Con ella crea doble y triple. Explica por qué doble "recuerda" el 2.

5. Decoradores y la sintaxis @

(Fuente: 393 - Understanding Python Decorator Functions and the @ Syntax.md)

🎯 Objetivo de aprendizaje

Escribir un decorador, aplicarlo con @ y explicar qué hace @app.route.

📝 Contenido original del curso

Un decorador es una función que envuelve a otra para darle funcionalidad adicional (antes, después o modificándola) sin cambiar su código. Usa las cuatro ideas de la sección 4: recibe una función, define otra adentro, la llama y la devuelve sin paréntesis.

Python
import time


def delay_decorator(function):
    def wrapper_function():
        time.sleep(2)
        # hacer algo antes
        function()
        function()
        # hacer algo después
    return wrapper_function


@delay_decorator
def say_hello():
    print("Hello")


def say_greeting():
    print("How are you?")


say_hello()   # espera 2 s e imprime "Hello" dos veces

El problema que resuelve: si quisieras un retraso en say_hello, say_bye y say_greeting, tendrías que copiar time.sleep(2) en las tres. Con el decorador, lo escribes una vez y lo aplicas donde quieras.

@ es azúcar sintáctico. Estas dos formas son equivalentes:

Python
@delay_decorator
def say_hello():
    print("Hello")
Python
def say_greeting():
    print("How are you?")

decorated_function = delay_decorator(say_greeting)
decorated_function()

La primera es más fácil de leer: al ver la función, ves de inmediato qué "filtro" tiene encima.

De vuelta a Flask: @app.route("/") es un decorador que le dice a Flask "cuando alguien pida la ruta /, ejecuta esta función". Si agregas

Python
@app.route("/bye")
def say_bye():
    return "Bye"

visitar /bye ejecuta say_bye. Es el framework quien decide qué función llamar, según la ruta.

📝 Ejercicio original del curso: el decorador que mide el tiempo

(Reconstruido a partir del final de la lección 393: "un ejercicio de codificación para crear sus propias funciones de decorador… medirá la cantidad de tiempo que tarda una función en ejecutarse". El enunciado completo no está entre los archivos suministrados.)

Crea un decorador que, al aplicarse a una función, la ejecute e imprima cuánto tiempo tardó.

🔍 Antes de programar

  • ¿Qué necesitas medir? El momento antes y el momento después de llamar a la función.
  • ¿Dónde va esa medición: en el decorador o en el wrapper? (Pista: ¿cuál se ejecuta cada vez que llamas a la función?)
  • ¿Cómo sabes el nombre de la función que estás midiendo? (Pista: las funciones tienen el atributo __name__.)

Una posible estructura, para que la completes:

Python
import time

def speed_calc_decorator(function):
    def wrapper():
        inicio = ...
        function()
        fin = ...
        print(f"{function.__name__} tardó {...:.3f} segundos")
    return wrapper


@speed_calc_decorator
def fast_function():
    for i in range(1_000_000):
        i * i


@speed_calc_decorator
def slow_function():
    for i in range(10_000_000):
        i * i


fast_function()
slow_function()

💡 Para medir tiempos cortos, time.perf_counter() es más preciso que time.time().

⚠️ Errores frecuentes

Error Qué ocurre Solución
return wrapper() en el decorador Se ejecuta la función al decorarla y el nombre queda valiendo None; luego TypeError: 'NoneType' object is not callable return wrapper, sin paréntesis
Olvidar return wrapper La función decorada queda como None Todo decorador devuelve el wrapper
Medir el tiempo en el decorador y no en el wrapper Mide cuánto tardó decorar, una sola vez La medición va dentro del wrapper
Poner @decorador() con paréntesis Se llama al decorador sin la función @decorador, sin paréntesis (salvo decoradores con parámetros, como @app.route("/"))

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1 — Aplicación: un decorador @repetir_tres que ejecute la función tres veces.
  • 🟡 Reto 2 — Ampliación: un decorador @anunciar que imprima "Empezando…" antes y "Listo." después.
  • 🟠 Reto 3 — Desafío: aplica dos decoradores a la misma función (@anunciar y @speed_calc_decorator). Cambia el orden. ¿Cambia lo que se imprime? Explica por qué.
  • 🔴 Reto 4 — Pensamiento computacional: en el Día 52 el bot necesitaba reintentar un clic cuando fallaba. Diseña (en papel o en código) un decorador @reintentar que vuelva a ejecutar una función si lanza una excepción, hasta 3 veces. ¿Qué pasa si la función necesita argumentos? (Lo resolverás el Día 55 con *args y **kwargs.)

6. 🚀 Proyecto del día: el servidor del salón

(Proyecto de integración creado para esta adaptación a partir de las lecciones 389 a 393.)

Problema

El salón necesita una "cartelera" en la red local: una página con el horario, los anuncios y un saludo, que el representante pueda actualizar editando un archivo Python.

Contexto

Hasta ahora tus páginas eran archivos HTML que se abrían con doble clic. Un servidor puede calcular la respuesta en el momento: la fecha de hoy, el día de la semana, el anuncio vigente.

Objetivo

Una aplicación Flask con varias rutas que devuelvan HTML generado con Python, y un decorador propio aplicado a las rutas.

Requisitos funcionales

  1. Ruta / con un saludo y la fecha de hoy (módulo datetime, Día 32).
  2. Ruta /horario con las clases de hoy según el día de la semana (un diccionario {"lunes": [...], ...}).
  3. Ruta /anuncio con un anuncio elegido al azar de una lista.
  4. Un decorador @registrar_visita que, cada vez que se visite una ruta, imprima en la terminal la hora y el nombre de la función.
  5. El servidor se puede arrancar con flask --app server run y con el botón Ejecutar.

Restricciones técnicas

  • Nombre del archivo: server.py (no flask.py).
  • Rutas definidas con @app.route.
  • app.run() dentro de if __name__ == "__main__":.

Planificación

  1. Comprender. 2. Entradas: la ruta que pide el navegador y la fecha del sistema. 3. Procesos: elegir la función según la ruta, calcular, construir el HTML. 4. Salidas: texto HTML. 5. Dividir: una función por ruta y un decorador. 6. Diseñar el diccionario del horario. 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar.

Construcción paso a paso

  • Versión 1: la ruta / con un texto fijo.
  • Versión 2: la fecha de hoy en la ruta /.
  • Versión 3: las rutas /horario y /anuncio.
  • Versión 4: el decorador @registrar_visita (escríbelo primero en un archivo aparte, sin Flask, y pruébalo con funciones normales).
  • Versión final: aplica el decorador a las rutas.

⚠️ Aviso para la versión final: al aplicar tu decorador a dos o más rutas aparecerá un error nuevo (AssertionError: View function mapping is overwriting an existing endpoint function: wrapper). Es esperado: lo entenderás y lo resolverás el Día 55 (sección 4). Mientras tanto, aplícalo a una sola ruta. (Si pruebas a poner @registrar_visita encima de @app.route(...), el error desaparece… pero las visitas dejan de registrarse, porque Flask guardó la función original antes de que tu decorador la envolviera. Compruébalo y explica por qué.)

Pruebas

Caso Resultado esperado
Visitar / Saludo y fecha de hoy
Visitar /horario un sábado Un mensaje del tipo "Hoy no hay clases", no un KeyError
Visitar /anuncio varias veces Anuncios distintos (a veces repetidos)
Visitar /no-existe Página 404 de Flask
Terminal Una línea por visita con hora y nombre de la ruta

🧪 Prueba y depuración

Python
from flask import Flask
app = Flask("__name__")

@app.route("/")
def inicio():
    return "Hola"

@app.route("/")
def horario():
    return "Horario"

if __name__ == "__main__":
    app.run

Encuentra tres problemas (ninguno produce un mensaje de error, lo que los hace más traicioneros):

  • "__name__" entre comillas: debe ser la variable. Con el texto, Flask no sabe dónde está tu archivo y busca las carpetas templates y static en la carpeta desde donde ejecutas (verificado); el Día 56 eso rompe las plantillas.
  • Dos funciones en la misma ruta /: Flask atiende con la primera y horario() nunca se ejecuta (verificado).
  • app.run sin paréntesis: nombra la función pero no la llama; el programa termina sin arrancar el servidor.

⭐ Extensión del proyecto

  • Ruta /cumpleanos que diga quién cumple años hoy (datos ficticios en un diccionario).
  • Leer los anuncios de un archivo anuncios.txt para que el representante no tenga que tocar el código.

7. 🌟 Proyecto extra: la caja de herramientas de decoradores

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

Tu grupo va a mantener varios programas del bloque de automatización (Días 45 a 53) y quiere funciones "comodín" reutilizables para medir, registrar y proteger funciones.

Requisitos

  1. Un módulo herramientas.py con al menos tres decoradores sin argumentos (por ejemplo: @cronometrar, @registrar que escriba en registro.txt la fecha y el nombre de la función, y @silenciar_errores que atrape cualquier excepción, la escriba en el registro y no detenga el programa).
  2. Un archivo demo.py que importe el módulo y lo use con al menos cuatro funciones.
  3. En herramientas.py, un bloque if __name__ == "__main__": con pruebas que solo se ejecuten al correr ese archivo directamente.

Decisiones que debes tomar (y justificar)

  • ¿Es buena idea @silenciar_errores? ¿En qué casos es peligroso esconder un error?
  • ¿En qué orden aplicarías @cronometrar y @registrar sobre la misma función?

Casos de prueba

  • Importar herramientas desde demo.py no ejecuta las pruebas internas.
  • Una función decorada con @silenciar_errores que hace 1 / 0 deja una línea en registro.txt y el programa continúa.

8. 🏗️ Proyecto integrador del bloque Flask — Etapa 1

(Proyecto creado para esta adaptación. Se construye por etapas entre los Días 54 y 58; la descripción completa está en el tutorial del Día 58.)

El Portal del Grupo. En los Días 41 a 44 construiste el sitio web de un grupo del colegio y en los Días 45 a 51 lo automatizaste. Ahora lo convertirás en una aplicación web servida por Python.

Etapa 1 (hoy):

  1. Crea la carpeta portal_grupo desde la terminal, con server.py adentro.
  2. Una aplicación Flask con las rutas / (bienvenida), /integrantes (lista de nombres ficticios) y /actividades.
  3. Un decorador @registrar_visita aplicado a una ruta (mira el aviso de la sección 6).
  4. Un README.md con los comandos exactos para ejecutarlo en tu sistema (flask --app server run).

9. 🧩 Comprueba lo aprendido

Conceptuales

  1. Con la analogía del restaurante, ¿qué parte es el cliente, cuál el servidor y cuál la base de datos?
  2. ¿En qué se diferencia una biblioteca de un framework? Da un ejemplo de cada uno.
  3. ¿Por qué no debes llamar a tu archivo flask.py?
  4. ¿Qué vale __name__ cuando ejecutas un archivo directamente? ¿Y cuando se importa?
  5. ¿Qué hace un decorador? ¿Qué significa que @ sea "azúcar sintáctico"?

Análisis de código

Python
def outer():
    print("A")
    def inner():
        print("B")
    print("C")
    return inner

f = outer()
f()
f()

¿Qué imprime y en qué orden?

Predicción

Python
def decorador(funcion):
    def wrapper():
        print("antes")
        funcion()
        print("después")
    return wrapper

@decorador
def saludar():
    print("hola")

print("inicio")
saludar()

Depuración

Python
def mi_decorador(funcion):
    def wrapper():
        funcion()
    return wrapper()

@mi_decorador
def hola():
    print("Hola")

hola()

¿Qué se imprime, qué error aparece y en qué línea está el problema?

Modificación

Cambia delay_decorator para que el número de segundos de espera se imprima antes de esperar ("Esperando 2 segundos…").

Creación

Desde la terminal (sin usar el Explorador de archivos), crea una carpeta mi_api, un archivo app.py con una ruta /ping que responda "pong" y ejecútalo solo con flask run. ¿Por qué aquí no necesitas --app?


10. 🤖 Programar con apoyo de IA

Ciclo de trabajo: formula el problema → inténtalo tú → consulta a la IA → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.

Actividad 1 — Comandos para tu terminal. Pide a una IA "cómo crear un archivo vacío desde la terminal". Si responde touch, pregúntate: ¿funciona en tu terminal? Pruébalo y corrígelo con la tabla de la sección 2.

Actividad 2 — Cómo arrancar Flask. Pregunta a una IA cómo ejecutar una aplicación Flask. Compara su respuesta con el Quickstart actual de la documentación oficial de Flask. ¿Usa set FLASK_APP o flask --app? ¿Cuál corresponde a la versión instalada (pip show flask)?

Actividad 3 — Explicar un decorador. Pide a una IA que explique @app.route("/"). Luego explícalo tú sin mirar su respuesta, usando las palabras "función", "envolver", "ruta" y "Flask la llama". Compara ambas explicaciones.


11. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
389 Ejecutar con export FLASK_APP=hello.py (Mac) o set FLASK_APP=hello.py (Windows) y luego flask run Sigue funcionando, pero la documentación actual de Flask 3 usa flask --app hello run; set no funciona en PowerShell ($env:FLASK_APP = "...") (verificado) Nota técnica de la sección 1
389 Error confuso al llamar requests.py a un archivo Python 3.13+ sugiere renombrarlo (verificado con 3.14) Nota técnica de la sección 1
390 "En su mayor parte, los mismos comandos sirven en Windows y en Mac" En el Símbolo del sistema no existen pwd, ls, touch ni rm -rf Tabla de equivalencias de la sección 2
390 Mostrar pwd en Windows En cmd, la carpeta actual se ve con cd sin argumentos; pwd existe en PowerShell Tabla de la sección 2
391 __name__ da "el nombre de la clase, función, método o descriptor actual" La variable __name__ de un archivo es el nombre del módulo; con flask run vale "hello", no "__main__" (verificado) Nota técnica de la sección 3
393 Ejercicio del decorador que mide el tiempo El enunciado no está entre los archivos suministrados Reconstruido en la sección 5 y marcado como tal

12. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Frontend y backend, full stack, lenguajes y frameworks, Flask contra Django, cliente/servidor/base de datos, analogía del restaurante 388 Sección 0 — Cliente y servidor desde los dos lados Secciones 6 y 8
Biblioteca contra framework, Quickstart, hello.py, conflicto de nombres, instalar con pip, FLASK_APP, flask run, servidor de desarrollo, HTML generado, Ctrl + C 389 Sección 1 Crear el servidor mínimo Retos 1–4 de la sección 1 Secciones 6 y 8
Kernel y shell, Terminal/zsh y Símbolo del sistema, pwd, ls, cd, Tab, mkdir, touch, rm, cd .., rm -rf y sus riesgos, cheat sheets 390 Sección 2 Crear y borrar carpetas y archivos Retos 1–4 de la sección 2; tabla de equivalencias Sección 8
Flask(__name__), __main__, random.__name__, if __name__ == "__main__": app.run() 391 Sección 3 — Tabla de valores de __name__ Secciones 6 y 7
Funciones como objetos de primera clase, calculate, funciones anidadas, devolver funciones, Python Tutor 392 Sección 4 Ejemplos de la calculadora Retos 1–4 de la sección 4 Sección 7
Decoradores, delay_decorator, wrapper_function, @ como azúcar sintáctico, @app.route, ruta /bye, ejercicio del decorador que mide el tiempo 393 Sección 5 Decorador de tiempo (reconstruido) Retos 1–4 de la sección 5 Secciones 6, 7 y 8
(No existe en el curso original) — — — Caja de herramientas de decoradores Sección 7
(No existe en el curso original) — — — Portal del Grupo, etapa 1 Sección 8

13. 📦 Evidencias de aprendizaje del Día 54

  • El servidor del salón funcionando, con la captura de la terminal mostrando las visitas registradas por el decorador.
  • La salida de flask --app server routes.
  • Tu "cheat sheet" de la terminal (Reto 4 de la sección 2) con comandos probados.
  • El decorador que mide el tiempo, aplicado a una función rápida y a una lenta.
  • La caja de herramientas de decoradores (proyecto extra).
  • La etapa 1 del Portal del Grupo con su README.md.
  • Cuaderno de errores: archivo con nombre de biblioteca, set en PowerShell, puerto ocupado, touch en cmd, "__main__" mal escrito, función con y sin paréntesis, return wrapper(), dos funciones en la misma ruta.