{ "version": "1", "metadata": { "marimo_version": "0.15.5" }, "cells": [ { "id": "Hbol", "code_hash": "1d0db38904205bec4d6f6f6a1f6cec3e", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [] }, { "id": "MJUe", "code_hash": "a40093b1686243c3c7cfe538710216ac", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [] }, { "id": "vblA", "code_hash": "e01ef05d140a83f20a1373bbc701f873", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [ { "type": "stream", "name": "stdout", "text": "\u2139\ufe0f /home/lucas/DataProyects/myrag/backend/.env ya tiene todas las claves necesarias.\n\u2705 Creado /home/lucas/DataProyects/myrag/backend/db/base.py\n\u2705 Creado /home/lucas/DataProyects/myrag/backend/db/session.py\n" } ] }, { "id": "bkHC", "code_hash": "9a66ffddaebf2517492a50f3cec46737", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [ { "type": "stream", "name": "stdout", "text": "\u2705 /home/lucas/DataProyects/Snippets_marimo_noteebooks/utils/ArquitectureLayer/Repo.py \u2192 domains/arquitecture_layer/Repo.py\n\u2705 /home/lucas/DataProyects/Snippets_marimo_noteebooks/utils/ArquitectureLayer/Mapper.py \u2192 domains/arquitecture_layer/Mapper.py\n\u2705 /home/lucas/DataProyects/Snippets_marimo_noteebooks/utils/ArquitectureLayer/Model.py \u2192 domains/arquitecture_layer/Model.py\n\u2705 Creado domains/arquitecture_layer/__init__.py\n" } ] }, { "id": "lEQa", "code_hash": "151311bdb1d09f9b69b11b735701f9e4", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [] }, { "id": "PKri", "code_hash": "0ec08d2138de5b6833360c8a32834465", "outputs": [ { "type": "data", "data": { "text/html": "
" } } ], "console": [] }, { "id": "Xref", "code_hash": "8c6feb2f5cc1800cca338765dc5f578d", "outputs": [ { "type": "data", "data": { "text/html": "" } } ], "console": [] }, { "id": "SFPL", "code_hash": "0ea082390576bd2a4e0921f150fd4810", "outputs": [ { "type": "data", "data": { "text/html": "

Prompt para la base del modelo:

Eres un asistente de modelado de datos.\nTu \u00fanica tarea es devolver JSON v\u00e1lido siguiendo exactamente esta estructura:\n
{\n  "CAMPOS": [\n    "columna: tipo_python | tipo_sqlalchemy | nullable={True/False} | default={valor/opcional} | comentarios='texto'"\n  ],\n  "RELACIONES": [\n    "campo_1 -> FK a public.campo_2 (ondelete='CASCADE/RESTRICT/SET NULL')"\n  ],\n  "INDEX_LIST": [\n    "nombre_indice"\n  ],\n  "UNIQUE_LIST": [\n    "nombre_unico"\n  ]\n}\n
\n

Reglas:

\n\n\ud83d\udccc Nombre de dominio: prueba_simple\n\ud83d\udcdd Explicaci\u00f3n: \n
\n\ud83c\udfaf Objetivo del prompt
\nGenera una lista de posibles campos para el modelo prueba_simple, en el siguiente formato:
\nFormato sugerido por l\u00ednea:\nnombre_columna: tipo_python | tipo_sqlalchemy(opcional) | nullable={True/False} | default={valor/opcional} | comentarios\n\nEjemplo esperado:\n
nombre: str | String(120) | nullable=False | comentarios="Nombre del prueba_simple"\ndescripcion: str | Text | nullable=True | default=None | comentarios="Descripci\u00f3n opcional"\nactivo: bool | Boolean | nullable=False | default=True | comentarios="Si el registro est\u00e1 activo"\n
\n
\n\ud83d\udd01 Posibles relaciones en formato:\n
---\n\n# Ejemplos.: \ncliente_id -> FK a public.clientes.id (ondelete='RESTRICT'),\nusuario_id -> FK a public.usuarios.id (ondelete='CASCADE')\n\n---\n\n"RELACIONES": "",\n
\n
\n\ud83d\udcd1 Posibles \u00edndices y restricciones:\n
"INDEX\\_LIST": "",   # Opcional\n"UNIQUE\\_LIST": "",  # Opcional\n
\n\u26a1 Nota: Usa tu criterio para proponer campos, relaciones y restricciones que sean coherentes con el dominio prueba_simple.
" } } ], "console": [] }, { "id": "BYtC", "code_hash": "f044700c1c88de78e20d7564b4eb324a", "outputs": [ { "type": "data", "data": { "text/html": "" } } ], "console": [] }, { "id": "RGSE", "code_hash": "2911dfde5af4f9ba3071d5b14ecba862", "outputs": [ { "type": "data", "data": { "text/html": "
Estado: JSON generado correctamente.
" } } ], "console": [] }, { "id": "Kclp", "code_hash": "451214e935d8c358cc438718919b1d21", "outputs": [ { "type": "data", "data": { "text/html": "
" } } ], "console": [] }, { "id": "emfo", "code_hash": "07ac0453e9734b104fe33b556c10bbdc", "outputs": [ { "type": "data", "data": { "text/html": "

Prompt para el modelo:

Te paso mis clases base para que heredes correctamente:\n1) Model_base \n\n2) Mapper_base\n\n3) Repo_base\n- Codigo de la clase Model: \n
# Repo.py\n\nfrom abc import ABC\nfrom typing import Type, TypeVar, Generic, Optional\nfrom sqlalchemy.orm import Session\nfrom sqlalchemy import func\nfrom datetime import datetime\nfrom datetime import datetime, timezone\n\nfrom .Mapper import Mapper_base  # Aseg\u00farate de importar tu ABC base\n\nTModelo = TypeVar("TModelo")\nTDominio = TypeVar("TDominio")\n\nclass Repo_base(ABC, Generic[TModelo, TDominio]):\n    def __init__(self, session: Session, modelo: type[TModelo], mapper: type[Mapper_base[TDominio, TModelo]]):\n        self.session = session\n        self.Modelo = modelo\n        self.Mapper = mapper\n\n    # ----------------------\n    # ADD\n    # ----------------------\n\n    def add(self, dominio: TDominio, created_by: Optional[str] = None, notes: Optional[str] = None) -> str:\n        data = self.Mapper.to_dict(dominio)\n        data.update({\n            "sys_created_by": created_by,\n            "sys_notes": notes,\n            "sys_version": 1\n        })\n        model = self.Modelo(**data)\n        self.session.add(model)\n        self.session.commit()\n        return model.id\n\n    def add_many(self, dominios: list[TDominio], created_by: Optional[str] = None, notes: Optional[str] = None) -> list[str]:\n        ids = []\n        for dominio in dominios:\n            data = self.Mapper.to_dict(dominio)\n            data.update({\n                "sys_created_by": created_by,\n                "sys_notes": notes,\n                "sys_version": 1\n            })\n            model = self.Modelo(**data)\n            self.session.add(model)\n            ids.append(model.id)\n        self.session.commit()\n        return ids\n\n    # ----------------------\n    # GET\n    # ----------------------\n\n    def get_by_id(self, id_: str) -> Optional[TDominio]:\n        model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()\n        return self.Mapper.from_model(model) if model else None\n\n    def get_all(self) -> list[TDominio]:\n        models = self.session.query(self.Modelo).filter_by(sys_deleted_at=None).all()\n        return self.Mapper.from_model_list(models)\n\n    def get_paginated(self, offset: int = 0, limit: int = 10) -> list[TDominio]:\n        models = self.session.query(self.Modelo).filter_by(sys_deleted_at=None).offset(offset).limit(limit).all()\n        return self.Mapper.from_model_list(models)\n\n    def get_deleted(self) -> list[TDominio]:\n        models = self.session.query(self.Modelo).filter(self.Modelo.sys_deleted_at.isnot(None)).all()\n        return self.Mapper.from_model_list(models)\n\n    # ----------------------\n    # UPDATE\n    # ----------------------\n\n    def update(self, id_: str, new_data: dict, updated_by: Optional[str] = None, notes: Optional[str] = None) -> bool:\n        model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()\n        if not model:\n            return False\n\n        for key, value in new_data.items():\n            if hasattr(model, key):\n                setattr(model, key, value)\n\n        model.sys_updated_by = updated_by\n        model.sys_notes = notes\n        model.sys_version = (model.sys_version or 1) + 1\n        self.session.commit()\n        return True\n\n    def bulk_update(self, updates: list[tuple[str, dict]], updated_by: Optional[str] = None, notes: Optional[str] = None) -> int:\n        count = 0\n        for id_, data in updates:\n            if self.update(id_, data, updated_by=updated_by, notes=notes):\n                count += 1\n        return count\n\n    # ----------------------\n    # DELETE\n    # ----------------------\n\n    def delete_by_id(self, id_: str) -> bool:\n        model = self.session.query(self.Modelo).filter_by(id=id_).first()\n        if model:\n            self.session.delete(model)\n            self.session.commit()\n            return True\n        return False\n\n    def delete_all(self) -> int:\n        deleted = self.session.query(self.Modelo).delete()\n        self.session.commit()\n        return deleted\n\n    # ----------------------\n    # SOFT DELETE\n    # ----------------------\n\n    def soft_delete(self, id_: str, deleted_by: Optional[str] = None, notes: Optional[str] = None) -> bool:\n        model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()\n        if model:\n            model.sys_deleted_at = datetime.now(timezone.utc)  # TZ-aware\n            model.sys_updated_by = deleted_by\n            model.sys_notes = notes\n            model.sys_version = (model.sys_version or 1) + 1\n            self.session.commit()\n            return True\n        return False\n\n    def soft_restore(self, id_: str, restored_by: Optional[str] = None, notes: Optional[str] = None) -> bool:\n        model = self.session.query(self.Modelo).filter_by(id=id_).first()\n        if model and model.sys_deleted_at is not None:\n            model.sys_deleted_at = None\n            model.sys_updated_by = restored_by\n            model.sys_notes = notes\n            model.sys_version = (model.sys_version or 1) + 1\n            self.session.commit()\n            return True\n        return False\n\n    # ----------------------\n    # OTROS\n    # ----------------------\n\n    def exists(self, id_: str) -> bool:\n        return self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first() is not None\n\n    def count(self) -> int:\n        return self.session.query(self.Modelo).filter_by(sys_deleted_at=None).count()\n
\n\n\ud83e\udde9 Tarea:\nGenera en un solo archivo las 3 piezas siguientes para la entidad prueba_simple:\n- Dominio (dataclass): prueba_simpleDom\n- Modelo ORM (SQLAlchemy): prueba_simpleModel\n- Mapper concreto: prueba_simpleMapper\n- Repo concreto (especializaci\u00f3n del gen\u00e9rico): prueba_simpleRepo\nAqui puedes ver como hacerlo con este ejemplo: \n
from pydantic import BaseModel, Field\n\nclass ItemIn(BaseModel):\n    nombre: str = Field(min_length=1)\n\nclass ItemOut(ItemIn):\n    id: int\n\n##############################\n\nfrom sqlalchemy import Column, String\nfrom backend.db.model_base import Model_base\n\nclass Item(Model_base):\n    __tablename__ = "item"  # opcional; si no, usa el nombre por defecto\n    __table_args__ = {"schema": "public"} # Usa el schema al final para evitar el error SchemaItem' object, such as a 'Column' or a 'Constraint' expected\n    nombre = Column(String, nullable=False, index=True)\n\n##############################\n\nfrom domains.ejemplo.domain import ItemIn, ItemOut\nfrom domains.ejemplo.model import Item\n\nclass ItemMapper:\n    @staticmethod\n    def to_model(d: ItemIn) -> Item:\n        return Item(nombre=d.nombre)\n\n    @staticmethod\n    def from_model(m: Item) -> ItemOut:\n        return ItemOut(id=m.id, nombre=m.nombre)\n\n    @staticmethod\n    def to_dict(d: ItemIn) -> dict:\n        return {"nombre": d.nombre}\n\n    @staticmethod\n    def from_dict(data: dict) -> ItemIn:\n        return ItemIn(**data)\n\n\n#############################\n\n\nfrom typing import Optional\nfrom sqlalchemy.orm import Session\nfrom domains.ejemplo.model import Item\nfrom domains.ejemplo.mapper import ItemMapper\nfrom domains.arquitecture_layer.Repo import Repo_base  # tu base\n\nclass ItemRepo(Repo_base[Item, "ItemIn|ItemOut"]):\n    def __init__(self, session: Session):\n        super().__init__(session=session, modelo=Item, mapper=ItemMapper)\n
\n\ud83e\uddf1 Esquema y tabla\nGen\u00e9rame un modelo SQLAlchemy que use table_args con constraints e \u00edndices, pero que siempre coloque el diccionario con schema al final de la tupla. No pongas el dict primero\n\nGuarda siempre en el esquema public y genera las relaciones con las tablas del esquema public\n\ud83d\uddc2\ufe0f Campos (excluyendo sys_*, que ya aporta Model_base):\nnombre: str | String(120) | nullable=false | comentarios='Nombre del prueba_simple'\ndescripcion: str | Text | nullable=true | default=null | comentarios='Descripci\u00f3n opcional'\nactivo: bool | Boolean | nullable=false | default=true | comentarios='Si el registro est\u00e1 activo'\n\ud83d\udd11 Clave primaria\n- Usa el ID autoincremental por defecto si est\u00e1 disponible. Si no, crea una columna id BigInteger autoincremental.\n\ud83d\udd17 Restricciones / \u00edndices (opcional)\n- uniques: \n- indexes: \n\ud83d\udd01 Relaciones (opcional)\n\ud83d\udd52 Zona horaria\n- Mant\u00e9n timestamps timezone-aware (ya lo hace Model_base).\n\ud83c\udfaf Reglas de salida / estilo\n- Devuelve SOLO c\u00f3digo Python v\u00e1lido (un \u00fanico archivo), sin comentarios fuera del c\u00f3digo ni explicaciones.\n- Importa exactamente desde mis rutas:\n from domains.arquitecture_layer.Repo import Repo_base import Model_base\n from domains.arquitecture_layer.Mapper import Mapper_base import Mapper_base\n from from domains.arquitecture_layer.Model import Model_base import Repo_base\n- Tipado fuerte (Python 3.10+), from __future__ import annotations opcional.\n- Dominio No puede ser @dataclass -> prueba_simpleDom.\n- Modelo ORM hereda de Model_base y (si existe) IdIntMixin.\n- Usa __table_args__ = {\"schema\": \"public\"}.\n- Tipos SQLAlchemy razonables (String(N), Integer, Numeric(p,s), Boolean, DateTime, JSON, etc.).\n- En Mapper:\n - to_model, from_model, to_dict, from_dict (respetando tipos; Numeric -> float en dominio).\n- Repo:\n - Clase prueba_simpleRepo(Repo_base[prueba_simpleModel, prueba_simpleDom]) con __init__(self, session) que fija Modelo y Mapper.\n - A\u00f1ade 1\u20133 m\u00e9todos de consulta comunes (ej.: get_by_nombre, buscar_por_rango_gasto, paginado por pa\u00eds), usando self.session.\n- No generes create_all ni conexi\u00f3n.\n- No dupliques columnas sys_*.\n- Mant\u00e9n nombre de columnas igual a las llaves del dominio y del dict del mapper.\n\ud83d\udce6 Salida esperada (estructura del archivo):\n- imports\n- dataclass de dominio prueba_simpleDom\n- clase ORM prueba_simpleModel\n- clase prueba_simpleMapper\n- clase prueba_simpleRepo\nGenera ahora el archivo .py final.
" } } ], "console": [] }, { "id": "Hstk", "code_hash": "35aa95aff52cef6e4d482b68709376a4", "outputs": [ { "type": "data", "data": { "text/html": "" } } ], "console": [] }, { "id": "nWHF", "code_hash": "f2bca22f61bdf4bb8998cfd495c2ab7b", "outputs": [ { "type": "data", "data": { "text/html": "
Estado: C\u00f3digo generado correctamente.
```python\nfrom sqlalchemy import Column, String, Text, Boolean\nfrom sqlalchemy.orm import Session\nfrom domains.arquitecture_layer.Model import Model_base\nfrom domains.arquitecture_layer.Mapper import Mapper_base\nfrom domains.arquitecture_layer.Repo import Repo_base\n\nclass prueba_simpleDom:\n    def __init__(self, nombre: str, descripcion: str = None, activo: bool = True):\n        self.nombre = nombre\n        self.descripcion = descripcion\n        self.activo = activo\n\nclass prueba_simpleModel(Model_base):\n    __tablename__ = "prueba_simple"\n    __table_args__ = (\n        {"schema": "public"}\n    )\n\n    nombre = Column(String(120), nullable=False, comment='Nombre del prueba_simple')\n    descripcion = Column(Text, nullable=True, default=None, comment='Descripci\u00f3n opcional')\n    activo = Column(Boolean, nullable=False, default=True, comment='Si el registro est\u00e1 activo')\n\nclass prueba_simpleMapper(Mapper_base[prueba_simpleDom, prueba_simpleModel]):\n    @staticmethod\n    def to_model(obj: prueba_simpleDom) -> prueba_simpleModel:\n        return prueba_simpleModel(\n            nombre=obj.nombre,\n            descripcion=obj.descripcion,\n            activo=obj.activo\n        )\n\n    @staticmethod\n    def from_model(model: prueba_simpleModel) -> prueba_simpleDom:\n        return prueba_simpleDom(\n            nombre=model.nombre,\n            descripcion=model.descripcion,\n            activo=model.activo\n        )\n\n    @staticmethod\n    def to_dict(obj: prueba_simpleDom) -> dict:\n        return {\n            "nombre": obj.nombre,\n            "descripcion": obj.descripcion,\n            "activo": obj.activo\n        }\n\n    @staticmethod\n    def from_dict(data: dict) -> prueba_simpleDom:\n        return prueba_simpleDom(\n            nombre=data["nombre"],\n            descripcion=data.get("descripcion"),\n            activo=data.get("activo", True)\n        )\n\nclass prueba_simpleRepo(Repo_base[prueba_simpleModel, prueba_simpleDom]):\n    def __init__(self, session: Session):\n        super().__init__(session=session, modelo=prueba_simpleModel, mapper=prueba_simpleMapper)\n\n    def get_by_nombre(self, nombre: str) -> list[prueba_simpleDom]:\n        models = self.session.query(self.Modelo).filter_by(nombre=nombre, sys_deleted_at=None).all()\n        return self.Mapper.from_model_list(models)\n\n    def get_activos(self) -> list[prueba_simpleDom]:\n        models = self.session.query(self.Modelo).filter_by(activo=True, sys_deleted_at=None).all()\n        return self.Mapper.from_model_list(models)\n\n    def get_inactivos(self) -> list[prueba_simpleDom]:\n        models = self.session.query(self.Modelo).filter_by(activo=False, sys_deleted_at=None).all()\n        return self.Mapper.from_model_list(models)\n
\n```
" } } ], "console": [] }, { "id": "iLit", "code_hash": "0179c0e694ba82777092d07dd6011946", "outputs": [ { "type": "data", "data": { "text/html": "" } } ], "console": [ { "type": "stream", "name": "stdout", "text": "\u2705 Archivo guardado en domains/prueba_simple.py\n" } ] }, { "id": "ZHCJ", "code_hash": "89b43fe4b144681748c9a0c927097b83", "outputs": [ { "type": "data", "data": { "text/html": "
Estado: Operaci\u00f3n completada correctamente.
" } } ], "console": [] }, { "id": "ROlb", "code_hash": "c378e574fe1d7ccd23a65046ceb20234", "outputs": [ { "type": "data", "data": { "text/html": "
Finalmente registrar las tablas en la bbdd
" } } ], "console": [] }, { "id": "qnkX", "code_hash": "f498a76a01492790d64917746fb4742d", "outputs": [ { "type": "data", "data": { "text/html": "" } } ], "console": [] }, { "id": "TqIu", "code_hash": "2392483aeff9ef5c1e45b1740c8e8da3", "outputs": [ { "type": "data", "data": { "text/html": "
Estado: Pulsa el bot\u00f3n para eliminar tablas vac\u00edas.
" } } ], "console": [] }, { "id": "Vxnm", "code_hash": "87ee1ef84a53699c468cc44edb65111c", "outputs": [ { "type": "data", "data": { "text/plain": "" } } ], "console": [] } ] }