El schema¶
Un schema es un fichero schemas/<nombre>.py dentro de un proyecto con .taisqlproject, y es
la única fuente de verdad: la base de datos, el cliente Python y los diagramas salen de él.
Anatomía de un fichero de schema¶
El orden importa, y es siempre este:
# -*- coding: utf-8 -*-
from __future__ import annotations # obligatorio: los schemas usan forward references
from tai_sql import *
from tai_sql.generators import *
datasource(...) # 1. con qué base de datos habla y cómo se interpreta lo declarado
generate(...) # 2. qué recursos produce
# 3. Enumerados 4. Tablas 5. Vistas
from __future__ import annotations no es opcional
Sin él, una relación que apunte a una clase declarada más abajo revienta al importar.
La referencia normativa del estilo es la plantilla que produce tai-sql new-schema
(default.py),
que además trae plantillas de dominio para autenticación y chatbots.
datasource() — con qué base de datos habla el schema¶
datasource(
provider=env('MAIN_DATABASE_URL'), # recomendado: la URL sale del entorno
schema='public', # nombre del schema EN la base de datos
syntax='v2', # cómo se interpreta lo que declaras ('v1' | 'v2')
operative_fields=False, # columnas de auditoría automáticas
secret_key_name='SECRET_KEY', # variable con la clave de cifrado
default_vector_dimensions=384, # dimensiones por defecto de una vector_column()
)
Declara dos cosas y solo dos: con qué base de datos habla el schema (provider) y cómo se
interpreta lo que declara (syntax, operative_fields, secret_key_name,
default_vector_dimensions) — más la configuración de backup, que es un inquilino aparte.
Las tres formas de declarar el provider¶
provider=env('MAIN_DATABASE_URL') # variable de entorno
provider=connection_string('postgresql://u:p@h:5432/db') # cadena literal
provider=params(host='localhost', port=5432, database='db', username='u', password='p')
Usa env() salvo que haya una razón muy concreta
El provider no es «el origen del CLI»: es el del schema, y lo heredan todos sus
consumidores. Con env() lo que se hereda es la indirección (os.getenv(...)), así que
el CLI en tu máquina y el cliente desplegado en producción pueden apuntar a bases de datos
distintas con una sola declaración. Con una cadena literal, la contraseña queda escrita en
el repositorio.
Campos operativos¶
Añade a todas las tablas created_at, updated_at, created_by y updated_by, y las
mantiene el framework: no las declares tú. El usuario que se escribe en created_by /
updated_by sale del contexto del cliente generado
(set_username()).
Se puede activar para una tabla suelta con __operative_fields__ = True en su cuerpo.
Los parámetros de pool¶
datasource() acepta además siete parámetros (pool_size, max_overflow, pool_timeout,
pool_recycle, pool_pre_ping, ssl, sqlalchemy_logs) que configuran el cliente
generado, no las conexiones de tai-sql. Se siguen aceptando por compatibilidad, pero su hogar
es el generador que produce el fichero donde acaban —PythonClientGenerator(pool_size=10)— y lo
que declare el generador manda.
Backup¶
backup_storage, backup_format, backup_retention y restore_provider configuran
tai-sql backup.
generate() — qué recursos produce el schema¶
generate(
PythonClientGenerator(
output_dir='database', # o una lista de directorios
mode='both', # 'sync' | 'async' | 'both'
max_depth=5, # profundidad al resolver relaciones anidadas
logger_name='tai-sql',
pool_size=10, max_overflow=5, pool_timeout=30, # del cliente generado
pool_recycle=3600, pool_pre_ping=True, ssl=True, sqlalchemy_logs=False,
),
ERDiagramGenerator(
output_dir='diagrams',
format='html', # 'html' | 'svg' | 'png' | 'pdf' | 'dot' | 'eps' | 'ps'
theme='classic', # 'classic' | 'modern' | 'dark' | 'minimal' | 'corporate'
include_views=True,
include_columns=True,
include_relationships=True,
include_triggers=True,
include_constraints=True,
include_enums=True,
include_operative_fields=False,
assets='inline', # solo html: 'inline' (offline) | 'cdn'
lines='ortho', size='auto', dpi=300, # solo formatos Graphviz
),
)
El cliente sale en <output_dir>/<schema>/ y expone <schema>_sync_api y <schema>_async_api.
Se puede declarar el mismo generador dos veces con formatos o destinos distintos, y también
escribir el tuyo.
Las dos sintaxis: v1 y v2¶
Las dos funcionan y las dos se soportan; las elige datasource(syntax=...). Mira el syntax=
del proyecto antes de escribir nada y usa la que ya se esté usando: mezclarlas en el mismo
fichero funciona, pero se lee fatal.
| Qué declaras | v1 (legacy) | v2 (recomendada) |
|---|---|---|
| Columna | id: int = column(primary_key=True) |
id: col[bigint] = column(primary_key=True) |
| Opcional | email: Optional[str] |
email: col[str \| None] |
| Uno a muchos | posts: List[Post] |
posts: onetomany[Post] |
| Muchos a uno | autor: Autor = relation(...) |
autor: manytoone[Autor] = relation(...) |
| Uno a uno | (no se puede expresar) | perfil: onetoone[Perfil] |
| Calculada | @calculated_column + -> float |
-> col[float], sin decorador |
La v2 dice la cardinalidad en el tipo, así que no hay que deducirla. En v1 se deduce —un hint
escalar es many-to-one y un List[X] es one-to-many—, y por eso v1 no sabe expresar un uno a
uno.
Compatibilidad
Lo que exporta tai_sql es la API con la que escribes tus schemas, y no se rompe: las
dos sintaxis conviven, y los cambios son aditivos o pasan por una deprecación explícita.