Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 71: Publicar tu aplicación web en Internet

100 Días de Código en Python — Día 71: Publicar tu aplicación web en Internet

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 71 (nivel avanzado).

Este tutorial adapta pedagógicamente el Día 71, que cierra el bloque de desarrollo web publicando el blog en Internet. Las secciones 📝 Contenido original del curso provienen de las lecciones 511 a 518; los archivos de inicio (day-71-starting-files-blog-for-deployment.zip) están en los recursos del curso. Los retos 🟢🟡🟠🔴, las secciones sobre probar en Windows, costos y límites de los planes gratuitos y publicar con responsabilidad, el proyecto extra y la etapa final de la Plataforma del Club 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, SQLAlchemy 2.1.1, psycopg 3.3.6, gunicorn 26.2.0 y waitress 3.0.2, se comprobó: que SQLAlchemy 2.1 usa psycopg 3 como controlador por defecto para postgresql://; que el paquete psycopg-binary que lista el requirements.txt del curso no instala el módulo psycopg (ModuleNotFoundError: No module named 'psycopg') y que la solución es psycopg[binary]; que postgres:// provoca NoSuchModuleError; y que gunicorn no funciona en Windows (ModuleNotFoundError: No module named 'fcntl'). También se revisaron los proveedores de la tabla del curso: la documentación de Render indica que los servicios gratuitos se detienen tras 15 minutos sin tráfico y que las bases PostgreSQL gratuitas caducan a los 30 días; Glitch redirige su página de precios a su blog (dejó de alojar proyectos) y Cyclic no respondió. No se creó ninguna cuenta ni se publicó ningún sitio.

🏫 Para el aula: publicar un sitio lo hace accesible a todo Internet. Antes de hacerlo, revisa con tu docente: ¿qué datos contiene?, ¿quién podrá registrarse?, ¿qué pasa si alguien publica algo inapropiado? Los proveedores exigen una edad mínima y, a menudo, una tarjeta de crédito para los planes pagos: la cuenta de publicación debe ser del docente o de la institución.


Tabla de contenidos

  1. Antes de empezar: del computador a Internet
  2. .gitignore y requirements.txt para producción
  3. Control de versiones desde PyCharm
  4. Variables de entorno para los secretos
  5. Un servidor WSGI: gunicorn
  6. Subir el proyecto a GitHub
  7. Elegir un proveedor y crear el servicio web
  8. De SQLite a PostgreSQL
  9. 📖 Publicar con responsabilidad
  10. 🚀 Proyecto del día: el blog en Internet
  11. 🌟 Proyecto extra: la lista de verificación de despliegue
  12. 🏗️ La Plataforma del Club — Etapa 13: publicación
  13. 🧩 Comprueba lo aprendido
  14. 🤖 Programar con apoyo de IA
  15. 🛠️ Correcciones técnicas al material original
  16. 📋 Matriz de trazabilidad
  17. 📦 Evidencias de aprendizaje del Día 71

0. Antes de empezar: del computador a Internet

(Fuente: 511 - Day 71 Goals - Learn to Deploy Your Website.md)

📝 Contenido original del curso

Hoy es "un momento crucial": el blog en Flask se publica en Internet, con una URL que cualquier persona del mundo puede abrir, incluso desde el celular. Para eso se combinan varias herramientas: Git y GitHub, un proveedor de hosting, gunicorn y el paso de la base de datos de desarrollo (SQLite) a una de producción (PostgreSQL).

Prerequisitos: el blog completo del Día 69, Git y GitHub (Día 70), variables de entorno (Días 35 y 61), SQLAlchemy (Día 63).

📖 Desarrollo contra producción (adaptación)

Desarrollo (hasta hoy) Producción (hoy)
Dónde corre Tu computador (127.0.0.1) Un servidor en Internet
Quién entra Solo tú Cualquiera
Servidor app.run(debug=True) gunicorn, sin depuración
Base de datos Archivo SQLite PostgreSQL
Secretos Un .env en tu carpeta Variables configuradas en el proveedor
Errores Página del depurador (Día 55) Página de error genérica; los detalles van a los registros (logs)

1. .gitignore y requirements.txt para producción

(Fuente: 512-add-a-gitignore-file-or-download-the-starting-files.md)

📝 Contenido original del curso

Puedes seguir con tu código del Día 69 o descargar los archivos de inicio. Si usas el tuyo, haz dos cambios:

  1. Agrega el .gitignore de los archivos de inicio, para no subir a GitHub archivos innecesarios, como posts.db, la carpeta venv o la carpeta oculta .idea de PyCharm.
  2. Reemplaza requirements.txt por el de los archivos de inicio, para que las versiones coincidan con las usadas para el despliegue y evitar errores (por ejemplo, la última versión de Flask no estaba disponible en render.com). Incluye además gunicorn y psycopg-binary, que se usarán más adelante. La información está actualizada al 13 de marzo de 2026, con Python 3.14:
Texto
Bootstrap_Flask==2.5.0
Flask_CKEditor==1.0.0
Flask_Login==0.6.3
Flask_WTF==1.2.2
WTForms==3.2.1
Werkzeug==3.1.6
Flask==3.1.3
Flask-SQLAlchemy==3.1.1
SQLAlchemy==2.1.0b1
gunicorn==25.1.0
psycopg-binary==3.3.3

📖 Explicación conceptual

Por qué fijar versiones (==). En tu computador instalaste ciertas versiones; el servidor instalará exactamente las del archivo. Si no las fijas, podría instalar otras más nuevas con cambios que rompan tu código.

🛠️ Nota técnica (verificado): con SQLAlchemy 2.1, una URL postgresql://... usa el controlador psycopg 3, que se importa como import psycopg. El paquete psycopg-binary solo contiene la parte compilada y no instala ese módulo: al conectarse aparece ModuleNotFoundError: No module named 'psycopg'. La línea correcta es:

Texto
psycopg[binary]==3.3.6

que instala psycopg y su versión compilada (verificado: la implementación queda en binary).

🛠️ Nota técnica: SQLAlchemy==2.1.0b1 es una versión beta (de prueba). En producción conviene una versión estable (en esta verificación, 2.1.1). Además, si agregaste email_validator o nh3 en los Días 61 y 67, deben estar en requirements.txt, o el sitio fallará en el servidor aunque funcione en tu computador.

Cómo generar tu propio requirements.txt con las versiones que usas de verdad (con el entorno virtual activo): python -m pip freeze > requirements.txt. Revísalo y quita lo que no sea del proyecto.


2. Control de versiones desde PyCharm

(Fuente: 513-use-git-to-add-version-control-to-your-project.md)

📝 Contenido original del curso

  1. Usa la versión final de tu blog o la copia de los recursos (instala los paquetes). La copia trae un administrador ya registrado: correo admin@email.com, contraseña asdf.
  2. Pon el proyecto bajo control de versiones, con la terminal o con la interfaz gráfica.
  3. En PyCharm: VCS → Enable Version Control Integration y elige Git: equivale a git init. Aparece un panel de Git con la consola y el registro.
  4. Los archivos se ponen rojos (sin seguimiento). Con la herramienta Commit, agrega los archivos sin versionar, escribe el mensaje y confirma. Lo que está en .gitignore (como .idea) queda excluido. Los archivos dejan de estar en rojo y el primer commit aparece en el registro.

📖 Explicación conceptual (adaptación)

La interfaz de PyCharm hace lo mismo que los comandos del Día 70 (git init, git add, git commit). Si usas VS Code, su panel "Control de código fuente" es equivalente.

🛠️ Nota técnica (seguridad): la base de datos de ejemplo tiene un administrador con la contraseña asdf. Sirve para probar en tu computador, pero nunca publiques un sitio con esa cuenta: cualquiera que haya hecho el curso la conoce. En el servidor empezarás con una base de datos vacía (sección 7) y registrarás tu administrador con una contraseña larga.


3. Variables de entorno para los secretos

(Fuente: 514-use-environment-variables-to-store-sensitive-information.md)

📝 Contenido original del curso

Nada de configuración ni datos sensibles dentro de main.py (variables de entorno, Día 35):

Python
import os

app.config['SECRET_KEY'] = os.environ.get('FLASK_KEY')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get("DB_URI", "sqlite:///posts.db")
  • Reemplaza cada app.config[...] con un valor sensible.
  • Si tu formulario de contacto envía correos, el correo y la contraseña también van en variables de entorno.
  • Desactiva la depuración:
Python
if __name__ == "__main__":
    app.run(debug=False)

📖 Explicación conceptual

os.environ.get("DB_URI", "sqlite:///posts.db") significa: "usa la variable DB_URI si existe; si no, usa SQLite". Así el mismo código funciona en tu computador (SQLite) y en el servidor (PostgreSQL), cambiando solo la configuración.

🛠️ Nota técnica (verificado): os.environ.get('FLASK_KEY') devuelve None si la variable no existe. En tu computador, sin la variable, los formularios fallarán con RuntimeError: A secret key is required to use CSRF. (Día 61). Para desarrollo, guarda las variables en un .env (que no se sube, Día 70) y cárgalas con python-dotenv, o usa un valor por defecto solo para desarrollo y haz que en producción sea obligatoria:

Python
app.config["SECRET_KEY"] = os.environ["FLASK_KEY"]   # si falta, el servidor no arranca: mejor que arrancar inseguro

4. Un servidor WSGI: gunicorn

(Fuente: 515-setup-a-wsgi-server-with-gunicorn.md)

📝 Contenido original del curso

Cada vez que ejecutabas la aplicación aparecía un aviso: el servidor de Flask es de desarrollo; en producción hay que usar un servidor WSGI. WSGI (Web Server Gateway Interface, PEP 3333) es el estándar que conecta una aplicación Python con el servidor web: "los servidores web normales no pueden ejecutar aplicaciones Python". Se usará el más popular: gunicorn.

  1. Verifica que gunicorn esté en requirements.txt (con tu código, agrega gunicorn==21.2.0).
  2. Crea en la carpeta principal un archivo llamado exactamente Procfile (con P mayúscula y sin extensión) con:
Texto
   web: gunicorn main:app
  • web: un proceso que recibe solicitudes HTTP.
  • gunicorn: el servidor que se usará.
  • main:app: el objeto app está en main.py (si tu archivo se llama distinto, cambia main).
  1. Guarda los cambios con un commit.

📖 ¿Qué hace un servidor de producción? (adaptación)

El servidor de desarrollo de Flask atiende una solicitud a la vez y está pensado para depurar. Un servidor de producción ejecuta varios procesos (workers), reinicia los que fallan y soporta muchas visitas simultáneas.

📖 🪟 Probar en Windows (adaptación, verificado)

🛠️ Nota técnica (verificado): gunicorn no funciona en Windows: al ejecutarlo aparece ModuleNotFoundError: No module named 'fcntl' (usa funciones propias de Linux y Mac). No es un error tuyo: el servidor del proveedor usa Linux, así que allí funcionará. Para probar en Windows un servidor de producción, usa waitress, que sí es compatible:

Terminal
python -m pip install waitress
waitress-serve --port=8000 main:app

y abre http://127.0.0.1:8000. Si funciona con waitress y sin debug, es muy probable que funcione con gunicorn en el servidor.

🛠️ Nota técnica: el Procfile lo usaba Heroku. En Render (el proveedor del video) lo que importa es el campo Start Command del panel (sección 6), donde se escribe el mismo gunicorn main:app. Tener el Procfile no hace daño y documenta cómo arrancar la aplicación.

⚠️ Errores frecuentes

Error Causa Solución
Failed to find attribute 'app' in 'main' El objeto no se llama app o el archivo no se llama main.py Ajusta main:app
ModuleNotFoundError: No module named 'fcntl' en tu PC gunicorn en Windows waitress para probar localmente
Procfile.txt Windows agregó la extensión Renómbralo sin extensión
El sitio funciona en tu PC pero no en el servidor Falta un paquete en requirements.txt Revisa los registros (logs) del proveedor

5. Subir el proyecto a GitHub

(Fuente: 516-push-to-your-remote-on-github.md)

📝 Contenido original del curso

  1. Crea una cuenta en GitHub si no la tienes.
  2. En la configuración de PyCharm, busca "github" y vincula tu cuenta.
  3. Con Git activo, el menú VCS se llama Git: Git → GitHub → Share Project on GitHub, elige un nombre de repositorio y pulsa Share. El código aparece en GitHub.

📖 Antes de pulsar "Share" (adaptación)

Revisa (Día 70):

  • git status: no debe aparecer .env, instance/, *.db ni venv/.
  • git log --all -- .env: debe estar vacío. Si alguna vez subiste un secreto, cámbialo antes de publicar.
  • ¿Repositorio público o privado? Render puede desplegar desde ambos.

6. Elegir un proveedor y crear el servicio web

(Fuente: 517-sign-up-to-a-hosting-provider-and-create-your-web-service.md)

📝 Contenido original del curso

Hay muchos proveedores, con precios y características que cambian. La tabla del curso:

Proveedor Costo aproximado al mes Plan
Heroku 5 dólares Eco y Basic
Render 0 Individual
Cyclic 0 Free Forever
Glitch 0 Starter
Vercel 0 Hobby
PythonAnywhere 0 Beginner

La mayoría despliega directamente desde un repositorio de GitHub. Pasos:

  1. Crear una cuenta (en Render, con GitHub: Authorize y confirmar el correo).
  2. New Web Service: elegir y conectar el repositorio del blog.
  3. Dejar casi todo por defecto; poner un nombre y cambiar el Start Command a gunicorn main:app.
  4. En Advanced, agregar la primera variable de entorno (FLASK_KEY).
  5. Crear el servicio. Todavía no funciona: falta la base de datos.

"Heroku eliminó su plan gratuito, pero otros proveedores todavía lo ofrecen".

📖 La tabla, hoy (adaptación, verificado el 27/09/2026)

🛠️ Nota técnica: los planes gratuitos cambian mucho, y la tabla ya no está al día:

  • Glitch: su página de precios redirige a su blog; dejó de alojar proyectos.
  • Cyclic: su sitio no respondió.
  • Vercel: está pensado para sitios estáticos y funciones, no para ejecutar gunicorn; publicar Flask allí requiere otra configuración.
  • PythonAnywhere: en la tabla del curso, su enlace apunta por error a la página de Vercel. Su dirección es pythonanywhere.com; tiene un plan gratuito para principiantes, pero no usa GitHub ni gunicorn de la misma forma (se configura desde su propio panel).
  • Render: según su documentación, los servicios gratuitos se detienen tras 15 minutos sin tráfico (la siguiente visita tarda en "despertar") y los cambios en su disco se pierden al detenerse; las bases PostgreSQL gratuitas caducan a los 30 días (hay 14 días de gracia para pasar a un plan pago).

Conclusión: antes de elegir, lee la página de precios actual del proveedor. Un plan gratuito sirve para aprender y mostrar un proyecto; no para un sitio del que dependan otras personas.


7. De SQLite a PostgreSQL

(Fuente: 518-upgrade-sqlite-database-to-postgresql.md)

📝 Contenido original del curso

SQLite es un archivo: es su fortaleza (puedes abrirlo con DB Browser mientras programas) y su debilidad: en proveedores como Heroku o Render, los archivos del servidor se reemplazan periódicamente (Heroku lo explicaba: más o menos cada 24 horas), así que la base de datos podría borrarse. Hay que pasar a PostgreSQL, que maneja millones de registros de forma confiable.

Como usamos SQLAlchemy, no hay que cambiar el código: solo crear la base PostgreSQL.

  1. En el menú del proveedor, crear una base Postgres con un nombre.
  2. Copiar la Internal Database URL (puede tardar un poco en estar lista).
  3. En la configuración del servicio web (Environment), crear la variable con el nombre que usa main.py (DB_URI) y pegar la URL.
  4. Cambiar el comienzo de postgres:// a postgresql://: SQLAlchemy lo exige.

Funciona porque el paquete psycopg es el adaptador de PostgreSQL para Python y trabaja junto con SQLAlchemy. Con la base vacía, registra el primer usuario (el administrador) y escribe la primera entrada. Ejemplo publicado por el curso: blog-deployment.onrender.com.

📖 Explicación conceptual (verificada)

URL Resultado con SQLAlchemy 2.1
postgres://usuario:clave@host/base NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
postgresql://usuario:clave@host/base Usa psycopg 3 (con psycopg[binary] instalado)
postgresql+psycopg://... Igual, pero indicando el controlador de forma explícita

¿Por qué "no hay que cambiar el código"? SQLAlchemy es un ORM (Día 63): tú escribes db.select(...) y él traduce al "dialecto" de SQL de cada base de datos. Las tablas se crean en PostgreSQL con el mismo db.create_all().

🛠️ Nota técnica: en lugar de corregir a mano postgres://, puedes hacerlo en el código, por si el proveedor entrega el formato antiguo:

Python
db_uri = os.environ.get("DB_URI", "sqlite:///posts.db")
if db_uri.startswith("postgres://"):
    db_uri = db_uri.replace("postgres://", "postgresql://", 1)
app.config["SQLALCHEMY_DATABASE_URI"] = db_uri

🛠️ Nota técnica: la URL de la base de datos incluye el usuario y la contraseña. Es un secreto: solo va en las variables de entorno del proveedor, nunca en el código ni en capturas de pantalla.

⚠️ Errores frecuentes

Error Causa Solución
NoSuchModuleError: ... postgres La URL empieza por postgres:// Cambia a postgresql://
ModuleNotFoundError: No module named 'psycopg' Solo instalaste psycopg-binary psycopg[binary] en requirements.txt
ModuleNotFoundError: No module named 'psycopg2' Una versión de SQLAlchemy 2.0 busca el controlador antiguo Usa postgresql+psycopg:// o instala psycopg2-binary
Los usuarios desaparecen al día siguiente Seguías usando SQLite en el servidor (DB_URI sin configurar) Configura la variable y revisa los registros
La base de datos dejó de funcionar al mes La base PostgreSQL gratuita caducó Plan pago o exportar los datos antes

8. 📖 Publicar con responsabilidad

(Sección creada para esta adaptación.)

Tu blog en Internet ya no es un ejercicio: es un servicio. Antes de compartir el enlace:

Pregunta Qué revisar
¿Quién puede registrarse? Si el primer usuario es el administrador (Día 69), regístrate tú primero, de inmediato
¿Qué pueden publicar los demás? Comentarios limpiados con nh3 (Día 69); un modo de borrar lo inapropiado
¿Qué datos guardas? Solo los necesarios; contraseñas con hash (Día 68)
¿La conexión es segura? Render ofrece HTTPS automáticamente; nunca publiques formularios de inicio de sesión sin HTTPS
¿Y si alguien lo ataca? debug=False, SECRET_KEY larga y aleatoria, dependencias actualizadas
¿Hay datos de menores? En Colombia, la Ley 1581 de 2012 protege los datos personales y exige especial cuidado con los de niños, niñas y adolescentes

9. 🚀 Proyecto del día: el blog en Internet

(Integra las lecciones 512 a 518.)

Problema

El blog del Día 69 solo existe en tu computador; nadie más puede leerlo.

Objetivo

Publicar el blog en un proveedor de hosting, con gunicorn, PostgreSQL y los secretos en variables de entorno.

Requisitos funcionales

  1. .gitignore completo y requirements.txt con versiones fijas (con psycopg[binary]).
  2. SECRET_KEY, DB_URI y datos del correo en variables de entorno; debug=False.
  3. Procfile o Start Command con gunicorn main:app.
  4. Repositorio en GitHub sin secretos.
  5. Servicio web y base PostgreSQL en el proveedor, conectados.
  6. Administrador registrado de inmediato con una contraseña fuerte.

Restricciones técnicas

  • Prueba local con waitress (Windows) o gunicorn (Mac/Linux) antes de publicar.
  • Ninguna URL de base de datos ni clave en el código, el historial o las capturas.

Planificación

  1. Comprender la diferencia entre desarrollo y producción. 2. Entradas: el proyecto y las credenciales. 3. Procesos: preparar → versionar → subir → configurar → conectar. 4. Salida: una URL pública. 5. Dividir en las 7 lecciones. 6. Diseñar la lista de variables de entorno. 7. Implementar. 8. Probar desde otro dispositivo. 9. Corregir con los registros. 10. Documentar en el README.

Construcción paso a paso

  • Versión 1: el proyecto con .gitignore y requirements.txt corregidos.
  • Versión 2: variables de entorno y prueba local con waitress.
  • Versión 3: en GitHub.
  • Versión 4: servicio web creado (todavía con error, sin base de datos).
  • Versión final: PostgreSQL conectado, administrador registrado y primera entrada publicada.

Pruebas

Caso Resultado esperado
Abrir la URL desde el celular (datos móviles) Portada del blog con HTTPS
Registrar un lector y comentar Funciona
Reiniciar el servicio desde el panel Usuarios, entradas y comentarios siguen ahí
Visitar /new-post como lector 403
Provocar un error (por ejemplo, /post/99999) Página 404 sin mostrar código ni el depurador
Buscar en GitHub la palabra postgresql:// Sin resultados en tu repositorio

🧪 Prueba y depuración

Python
app.config['SECRET_KEY'] = '8BYkEfBA6O6donzWlSihBXox7C0sKR6b'
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://blog_user:Xk9...@dpg-abc/blog'

if __name__ == "__main__":
    app.run(debug=True)
Texto
# requirements.txt
flask
gunicorn
psycopg-binary

Encuentra cinco problemas: la clave secreta y la URL de la base de datos están en el código; la URL empieza por postgres://; debug=True; las versiones no están fijadas; y psycopg-binary no instala el módulo psycopg.

⭐ Extensión del proyecto

  • Configurar un dominio propio (si la institución tiene uno) en el proveedor.
  • Revisar los registros (logs) del servicio y explicar tres líneas.
  • Hacer una copia de seguridad de la base PostgreSQL antes de que caduque el plan gratuito.

10. 🌟 Proyecto extra: la lista de verificación de despliegue

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

Situación

Cada vez que alguien del grupo publica un proyecto se le olvida algo distinto (la clave en el código, un paquete, la depuración encendida). Quieren una herramienta que lo revise antes de publicar.

Requisitos

  1. Un script revisar_despliegue.py que, dada la carpeta de un proyecto Flask, compruebe y reporte con ✅/❌:
    • que exista .gitignore y contenga .env, venv e instance;
    • que requirements.txt exista y todas las líneas tengan ==;
    • que no aparezca debug=True en los .py;
    • que no haya cadenas parecidas a claves (por ejemplo, SECRET_KEY = " seguido de texto, o postgres:// con contraseña) en los .py;
    • que exista Procfile o un README que diga cómo arrancar la aplicación;
    • que no esté psycopg-binary sin psycopg.
  2. Un resumen final: cuántos puntos se cumplen y cuáles faltan.
  3. Pruébalo con dos proyectos: uno "bueno" y uno con errores a propósito.

Decisiones que debes tomar (y justificar)

  • ¿Cómo evitas falsos positivos (por ejemplo, un comentario que dice debug=True)?
  • ¿Qué revisiones no puede hacer un script y requieren a una persona?

Casos de prueba

  • Un proyecto con app.run(debug=True) → ❌ en esa revisión.
  • Un requirements.txt con flask sin versión → ❌.
  • El proyecto "bueno" → todo ✅.

11. 🏗️ La Plataforma del Club — Etapa 13: publicación

(Proyecto integrador del bloque avanzado, descrito completo en el Día 70. Esta es su etapa adicional.)

  1. Prepara la Plataforma para producción: variables de entorno, debug=False, requirements.txt con versiones y psycopg[binary].
  2. Pásala por tu revisar_despliegue.py.
  3. Con autorización del docente, publícala con una cuenta de la institución y PostgreSQL, solo con datos ficticios, y compártela con el grupo durante una semana.
  4. Recoge comentarios de los usuarios y registra en el README qué cambió entre la versión local y la publicada.
  5. Al terminar el periodo de prueba, exporta los datos y da de baja el servicio si no se va a mantener.

12. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Qué diferencias hay entre desarrollo y producción?
  2. ¿Qué es WSGI y por qué se necesita gunicorn?
  3. ¿Por qué SQLite no sirve en la mayoría de proveedores de hosting?
  4. ¿Por qué no hay que cambiar el código para pasar de SQLite a PostgreSQL?
  5. ¿Qué información nunca debe estar en el repositorio?

Análisis de código

Python
db_uri = os.environ.get("DB_URI", "sqlite:///posts.db")

¿Qué valor tiene db_uri en tu computador sin la variable? ¿Y en el servidor con la variable configurada?

Predicción

Escribes en el Start Command gunicorn app:main, pero tu archivo es main.py y el objeto se llama app. ¿Qué pasa?

Depuración

El sitio publicado muestra "Internal Server Error" y en los registros aparece No module named 'psycopg'. ¿Qué cambias?

Modificación

Cambia la configuración para que el sitio no arranque si falta FLASK_KEY, en lugar de arrancar con None.

Creación

Escribe una ruta /salud que devuelva {"estado": "ok", "base_de_datos": "..."} con el nombre del motor de base de datos en uso (pista: db.engine.dialect.name), útil para comprobar que el servidor usa PostgreSQL.


13. 🤖 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 — El requirements.txt de la IA. Pide a una IA un requirements.txt para desplegar Flask con PostgreSQL. ¿Usa psycopg2-binary, psycopg-binary o psycopg[binary]? Compruébalo contra la documentación de psycopg y la de dialectos de SQLAlchemy.

Actividad 2 — ¿Gratis para siempre? Pregunta a una IA qué proveedores ofrecen hosting gratuito para Flask. Verifica cada respuesta en la página actual de precios del proveedor. ¿Alguno ya no existe?

Actividad 3 — Leer registros. Copia (sin datos sensibles) un error de los registros de tu servicio y pide a una IA que lo explique. Comprueba su hipótesis antes de cambiar el código.


14. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
512, 518 psycopg-binary==3.3.3 como adaptador de PostgreSQL No instala el módulo psycopg; con SQLAlchemy 2.1 falla con ModuleNotFoundError (verificado). Se usa psycopg[binary] Nota técnica de la sección 1
512 SQLAlchemy==2.1.0b1 Es una versión beta; en producción, una estable Nota técnica de la sección 1
515 Usar gunicorn No funciona en Windows (fcntl, verificado); se prueba con waitress Sección 4
515 Procfile Lo usa Heroku; en Render cuenta el Start Command Nota técnica de la sección 4
514 os.environ.get('FLASK_KEY') Devuelve None sin la variable y los formularios fallan (verificado) Nota técnica de la sección 3
517 Tabla de proveedores gratuitos Glitch ya no aloja proyectos; Cyclic no respondió; el enlace de PythonAnywhere apunta a Vercel; Render detiene servicios gratuitos y su PostgreSQL gratuito caduca a los 30 días (verificado) Sección 6
513 Administrador admin@email.com / asdf Contraseña débil y conocida; no publicar con esa cuenta Nota técnica de la sección 2
518 Cambiar postgres:// por postgresql:// Correcto (verificado: NoSuchModuleError); se puede automatizar en el código Sección 7
511 "Bienvenidos al día 70" y mención de Heroku Es el Día 71; el despliegue del curso se hace en Render —

15. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Publicar el blog, Git y GitHub, hosting, gunicorn, SQLite a PostgreSQL 511 Sección 0 — Desarrollo contra producción Sección 9
.gitignore de los archivos de inicio, requirements.txt con versiones, gunicorn y psycopg-binary 512 Sección 1 Preparar el proyecto psycopg[binary], pip freeze Secciones 9, 10 y 11
Control de versiones en PyCharm, administrador de ejemplo, Enable Version Control, Commit 513 Sección 2 Primer commit Contraseña del administrador Sección 9
os.environ.get, FLASK_KEY, DB_URI con valor por defecto, correo, debug=False 514 Sección 3 Variables de entorno Variable obligatoria Secciones 9, 10 y 11
WSGI, PEP 3333, gunicorn, Procfile, web: gunicorn main:app 515 Sección 4 Configurar gunicorn Probar en Windows con waitress Secciones 9 y 10
Vincular PyCharm con GitHub, Share Project on GitHub 516 Sección 5 Subir a GitHub Revisión previa de secretos Sección 9
Tabla de proveedores, Render, New Web Service, Start Command, variables en Advanced 517 Sección 6 Crear el servicio web Estado actual de los proveedores Secciones 9 y 11
SQLite como archivo, PostgreSQL, Internal Database URL, variable DB_URI, postgres:// a postgresql://, psycopg, sitio de ejemplo 518 Sección 7 Conectar PostgreSQL Corrección automática de la URL Secciones 9 y 11
(No existe en el curso original) — Sección 8 — Publicar con responsabilidad Secciones 9 y 11
(No existe en el curso original) — — — Lista de verificación de despliegue Sección 10
(No existe en el curso original) — — — Plataforma del Club, etapa 13 Sección 11

16. 📦 Evidencias de aprendizaje del Día 71

  • La URL del blog publicado (o, si no se publicó, la captura de la prueba local con waitress y sin depuración).
  • Tu requirements.txt final y la lista de variables de entorno (solo los nombres).
  • La prueba de reinicio: los datos siguen ahí.
  • revisar_despliegue.py con su informe sobre dos proyectos (proyecto extra).
  • La etapa 13 de la Plataforma del Club y la reflexión sobre la semana de prueba.
  • Cuaderno de errores: psycopg-binary sin psycopg, postgres://, gunicorn en Windows, Procfile.txt, SECRET_KEY en None, paquetes que faltan en el servidor, SQLite en producción, base gratuita caducada, debug=True publicado.