Administrar el engine y la sesión SQLAlchemy
Objetivos
Vas a crear una conexión reutilizable y asegurar que la sesión usada por una solicitud se cierre incluso si ocurre un error.
Concepto
Un Engine conoce el dialecto y administra conexiones reutilizables; SQLAlchemy normalmente abre la conexión física cuando una operación la necesita. Una Session representa el trabajo de una solicitud con el ORM. No conviene crear un engine por cada fila ni dejar sesiones abiertas.
URL.create recibe por separado el host, usuario y contraseña, así no tienes que unir texto que incluya caracteres especiales. FastAPI permite usar una dependencia con yield: entrega la sesión a la ruta y luego ejecuta el cierre. Para una API de solo lectura no llamaremos a commit().
Ejemplo
Este patrón se ubica en app/database.py. Los valores se leen de las variables locales preparadas en la lección anterior:
import os
from functools import lru_cache
from dotenv import load_dotenv
from sqlalchemy import URL, create_engine
from sqlalchemy.orm import Session, sessionmaker
@lru_cache(maxsize=1)
def get_session_factory() -> sessionmaker[Session]:
load_dotenv()
required = ("DB_USER", "DB_PASSWORD")
missing = [name for name in required if not os.getenv(name)]
if missing:
raise RuntimeError("Faltan variables: " + ", ".join(missing))
url = URL.create(
"mysql+pymysql",
username=os.environ["DB_USER"],
password=os.environ["DB_PASSWORD"],
host=os.getenv("DB_HOST", "127.0.0.1"),
port=int(os.getenv("DB_PORT", "3306")),
database=os.getenv("DB_NAME", "sakila"),
)
engine = create_engine(url, pool_pre_ping=True)
return sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)
def get_db():
with get_session_factory()() as session:
yield session@lru_cache conserva la fábrica para que la aplicación no construya un engine nuevo por petición. El with cierra la sesión al salir. Consulta la documentación de engine y URL, sesiones y dependencias con yield.
Práctica guiada
- Identifica qué función se ejecuta una vez gracias al caché.
- Sigue el valor que la dependencia entrega a la ruta.
- Encuentra el contexto que cierra la sesión.
Reto
Agrega DB_PORT con valor predeterminado 3306 y entrega port como entero a URL.create.
Intenta el reto antes de consultar las pistas.
Quiz
Comprobación
El ciclo de vida de la sesión está definido. La siguiente lección limita la cuenta MySQL para que el diseño de solo lectura también tenga una barrera en la base.