Saltar a contenido

Los DTOs

Por cada tabla se generan varios modelos Pydantic 2. Son objetos normales, sin sesión detrás: se pueden serializar, devolver desde una API y guardar sin miedo a un DetachedInstanceError.

DTO Para qué
<Modelo>Read Lo que devuelven las lecturas. Incluye las relaciones cargadas
<Modelo>Create Datos de alta. Admite relaciones anidadas
<Modelo>Filter Filtros; cuáles, según el tipo de cada columna
<Modelo>UpdateValues Los valores a escribir en un update
<Modelo>Update filter + values, para update_many
<Modelo>UpdateNested Actualización de una relación anidada
<Modelo>DataFrameValidator Valida un DataFrame contra el esquema de la tabla

Read

usuario = public_sync_api.usuario.find(id=1, includes=['posts'])

usuario.nombre               # los campos, tipados
usuario.posts                # List[PostRead], si se pidió en includes
usuario.model_dump()         # dict
usuario.model_dump_json()    # str
usuario.to_dict()            # dict, omitiendo los None

Incluye las columnas calculadas y las columnas de auditoría, si el schema las declara. Las columnas cifradas vienen descifradas y las vectoriales solo si se pidieron.

Create

UsuarioCreate(nombre='Ana', email='ana@x.com')

PostCreate(
    titulo='Hola',
    contenido='...',
    autor=UsuarioCreate(nombre='Ana', email='ana@x.com'),   # relación anidada
)

Las columnas con default, las opcionales y las autoincrementales no hacen falta. Los campos que no existen en la tabla se rechazan (extra='forbid'): un typo en el nombre de un campo es un error de validación, no un valor que se pierde en silencio.

Filter y Update

from database.public import UsuarioFilter, UsuarioUpdateValues, UsuarioUpdate

public_sync_api.usuario.update_many(UsuarioUpdate(
    filter=UsuarioFilter(activo=False, min_creado_en=datetime(2026, 1, 1)),
    values=UsuarioUpdateValues(activo=True),
))

UpdateValues solo lleva lo que quieras escribir: lo que no declares no se toca.

Enumerados

Los campos de tipo enumerado se validan contra sus valores:

PostCreate(titulo='Hola', contenido='...', estado='publicado')   # ✅
PostCreate(titulo='Hola', contenido='...', estado='inventado')   # ValidationError

Y los valores posibles se consultan en la fachada:

public_sync_api.estado.find_many()   # ['borrador', 'publicado', 'archivado']

Usarlos con FastAPI

Son modelos Pydantic estándar, así que encajan directamente:

from fastapi import FastAPI
from database.public import public_sync_api, UsuarioRead, UsuarioCreate

app = FastAPI()

@app.get('/usuarios', response_model=list[UsuarioRead])
def listar(limit: int = 50):
    return public_sync_api.usuario.find_many(limit=limit)

@app.post('/usuarios', response_model=UsuarioRead)
def crear(usuario: UsuarioCreate):
    return public_sync_api.usuario.create(usuario)