Tu primer proyecto¶
De cero a un cliente generado funcionando. Necesitas tai-sql instalado y una base de datos PostgreSQL a la que puedas conectarte.
1. Crear el proyecto¶
Eso deja esto:
mi_proyecto/
├── .taisqlproject # {"name": "mi_proyecto", "default_schema": "public"}
├── pyproject.toml # dependencias del CLIENTE GENERADO
├── README.md
├── __init__.py
├── schemas/
│ └── public.py # el schema: la única fuente de verdad
├── views/
│ └── public/
│ └── user_stats.sql # el SQL de cada vista, un fichero por vista
└── diagrams/
schemas/public.py viene con un ejemplo completo —un blog con usuarios, posts, comentarios, un
enumerado, un trigger, un feed() y una vista— que sirve de plantilla. Bórralo y escribe el
tuyo cuando hayas visto cómo funciona.
2. Configurar la conexión¶
El nombre de la variable es el que diga el env(...) del schema. tai-sql no carga .env por
su cuenta: si usas uno, cárgalo tú antes (por ejemplo con direnv, o set -a; . .env; set +a).
Si la base de datos no existe, tai-sql push la crea.
3. Instalar las dependencias del proyecto¶
4. Escribir el schema¶
Abre schemas/public.py y déjalo así:
# -*- coding: utf-8 -*-
from __future__ import annotations
from tai_sql import *
from tai_sql.generators import *
datasource(
provider=env('MAIN_DATABASE_URL'),
schema='public',
syntax='v2',
operative_fields=True, # created_at / updated_at / created_by / updated_by
)
generate(
PythonClientGenerator(output_dir='database'),
ERDiagramGenerator(output_dir='diagrams', format='html'),
)
class Estado(Enum):
"""Estado de publicación de un post."""
BORRADOR = 'borrador'
PUBLICADO = 'publicado'
class Usuario(Table):
"""Usuarios del sistema."""
__tablename__ = 'usuario'
id: col[int] = column(primary_key=True, autoincrement=True)
nombre: col[str] = column(description='Nombre visible')
email: col[str] = column(unique=True)
posts: onetomany[Post]
def feed(self) -> List[Usuario]:
"""Datos iniciales. Se ejecutan con: tai-sql feed"""
return [Usuario(nombre='Ana', email='ana@example.com')]
class Post(Table):
"""Posts publicados."""
__tablename__ = 'post'
id: col[bigint] = column(primary_key=True, autoincrement=True)
titulo: col[str]
contenido: col[text]
estado: col[Estado] = column(default=Estado.BORRADOR)
publicado_en: col[datetime | None]
autor_id: col[int]
autor: manytoone[Usuario] = relation(
fields=['autor_id'], references=['id'], backref='posts'
)
@on_update(timing='before', fields=['estado'],
when=lambda t: t.new.estado == 'publicado')
def sellar_publicacion(self, t: TriggerAPI[Post]):
"""Al pasar a publicado, sella la fecha."""
t.new.publicado_en = datetime.now()
from __future__ import annotations no es opcional
Sin él, posts: onetomany[Post] revienta al importar, porque Post todavía no existe en
esa línea. Es el error de schema más común.
5. Ver qué implicaría, y aplicarlo¶
Eso imprime el DDL exacto que se ejecutaría, sin tocar nada. Cuando te convenza:
push sincroniza la base de datos y, al terminar, regenera el cliente y el diagrama.
6. Poblar con los datos iniciales¶
7. Usar el cliente¶
from database.public import public_sync_api, UsuarioCreate, PostCreate, set_username
set_username('ana') # va a created_by / updated_by
usuario = public_sync_api.usuario.upsert(
UsuarioCreate(nombre='Ana', email='ana@example.com'),
match_fields=['email'],
)
public_sync_api.post.create(
PostCreate(titulo='Hola mundo', contenido='...', autor_id=usuario.id)
)
for post in public_sync_api.post.find_many(includes=['autor'], limit=10):
print(post.titulo, '→', post.autor.nombre)
Y el diagrama está en diagrams/public.html: ábrelo con doble clic.
El ciclo, a partir de aquí¶
vim schemas/public.py # 1. cambias el schema
tai-sql push --dry-run --verbose # 2. miras qué implicaría
tai-sql push # 3. lo aplicas (regenera el cliente al terminar)
Si solo quieres regenerar el cliente sin tocar la base de datos, tai-sql generate.
Nunca edites el código generado
Todo lo que hay bajo el output_dir es desechable: se borra y se regenera. Si el cliente no
hace lo que necesitas, el cambio va en el schema.
¿Y si la base de datos ya existe?¶
Entonces no empiezas escribiendo el schema, sino leyéndolo:
Ver tai-sql pull.