Lectura¶
find — por clave primaria¶
usuario = dao.find(id=1) # UsuarioRead | None
usuario = dao.find(id=1, includes=['posts'])
usuario = dao.find(id=1, includes=['posts', 'posts.comentarios'])
| Parámetro | Para qué |
|---|---|
| la clave primaria | Se llama como la columna (id=, codigo=…) |
includes |
Relaciones a cargar |
rls |
Regla de filtrado por fila |
session |
Participar en una transacción existente |
find_many — con filtros¶
usuarios = dao.find_many(
limit=10, offset=20,
order_by=['creado_en', 'nombre'], order='DESC',
nombre='Ana', # igualdad
in_email=['a@x.com', 'b@x.com'], # IN (OR lógico)
min_creado_en=datetime(2026, 1, 1), # >= (incluido)
max_creado_en=datetime(2026, 12, 31),
includes=['posts'],
)
Qué filtros existen para cada columna¶
Los genera el tipo de la columna, no una convención genérica:
| Tipo | Filtros generados |
|---|---|
int, bigint, date |
<campo>, in_<campo>, min_<campo>, max_<campo> |
str, text |
<campo>, in_<campo> |
float, numeric, datetime, time |
min_<campo>, max_<campo> |
bool |
<campo> |
dict, largebinary |
ninguno |
Las claves primarias y foráneas siempre reciben <campo> e in_<campo>, sea cual sea su
tipo.
Búsqueda por patrón
En las columnas de texto, el filtro de igualdad acepta patrones con % y se resuelve como
ILIKE:
Los min_/max_ son inclusivos (>= y <=), y los filtros se combinan con AND. Para un
OR sobre valores de la misma columna, in_<campo>.
Relaciones: includes¶
dao.find_many(includes=['autor']) # una relación
dao.find_many(includes=['autor', 'autor.empresa']) # anidada, con notación de punto
dao.find_many(includes=['autor', 'comentarios']) # varias
Sin includes, las relaciones no vienen cargadas. Con él, se cargan de forma optimizada —una
consulta, no N+1— hasta el max_depth que declare el generador (5 por defecto).
Orden y paginación¶
dao.find_many(order_by=['creado_en'], order='DESC')
dao.find_many(limit=10) # los 10 primeros
dao.find_many(limit=10, offset=20) # la tercera página de 10
order se aplica a todas las columnas de order_by, y solo tiene efecto si hay order_by.
count y exists¶
Aceptan los mismos filtros que find_many. exists cuenta por dentro y compara con cero, así
que cuesta lo mismo que count: elígelo por legibilidad, no por rendimiento.
Lo que devuelve¶
Un DTO Pydantic <Modelo>Read, no el modelo de SQLAlchemy. Es un objeto normal, sin sesión
detrás: se puede serializar, devolver por una API o guardar, y no lanza DetachedInstanceError.
usuario = dao.find(id=1, includes=['posts'])
usuario.nombre # str
usuario.posts # List[PostRead], ya cargada
usuario.model_dump() # dict
usuario.model_dump_json() # str
Las columnas cifradas vienen descifradas, y
las vectoriales no vienen salvo que las pidas con include_vectors=True.