Saltar a contenido

Lectura

dao = public_sync_api.usuario

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:

dao.find_many(nombre='An%')     # todos los que empiezan por "An"

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

dao.count(activo=True)          # int
dao.exists(email='ana@x.com')   # bool

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.