Skip to content

feat(orm): modelos POO con anotaciones tipadas (Fase 1.5)#7

Merged
ShibaRoPinoo merged 1 commit into
feature/fase-1-query-ricofrom
feature/fase-1-5-orm
May 18, 2026
Merged

feat(orm): modelos POO con anotaciones tipadas (Fase 1.5)#7
ShibaRoPinoo merged 1 commit into
feature/fase-1-query-ricofrom
feature/fase-1-5-orm

Conversation

@ShibaRoPinoo
Copy link
Copy Markdown
Owner

Resumen

ORM ligero sobre el QueryBuilder de #6. Estilo híbrido: las anotaciones Python dan el tipo, fields.X(...) añade metadata extra cuando hace falta.

class User(Model):
    __table__ = \"users\"
    id: int = fields.PrimaryKey()
    name: str
    email: str = fields.String(unique=True)
    age: int | None = None
    settings: dict = fields.Json(default_factory=dict)
    created_at: datetime = fields.DateTime(default_now=True)

shiba.set_default_connection(cx)
User.create_table()
user = User(name=\"John\"); user.save()
User.find(1)
User.where(\"age\", \">\", 18).get()  # → list[User]

Highlights

  • shiba/orm/fields.py con 12 subclases (PrimaryKey, String, Integer, Boolean, Json, DateTime, DecimalField, Enum, ForeignKey, ...) y motor de inferencia que entiende T | None, dict, list, builtins y datetime.
  • Model con metaclass que lee anotaciones por MRO con inspect.get_annotations(eval_str=True) — soporta from __future__ import annotations.
  • ModelQuery[T] que delega al QueryBuilder y hidrata cada fila al modelo.
  • Conexión inyectable: global (set_default_connection(cx)) o por clase (__db__).
  • ErrorCode.raise_ ahora se tipa como NoReturn.

Test plan

  • 16 tests nuevos cubren inferencia, save (INSERT con LAST_INSERT_ID + UPDATE), delete, find, where + order_by con hidratación, JSON roundtrip, create_table DDL, validación de PK obligatoria y kwargs desconocidos
  • Total 100/100. ruff y mypy --strict limpios
  • Smoke contra MySQL real → llega con el PR de Testcontainers

Apilado sobre #6. Mergear después de #5 y #6.

🤖 Generated with Claude Code

Añade un ORM ligero sobre el `QueryBuilder` ya existente. La identidad
del producto: declarar un modelo es declarar tipos Python, no DSL.

API
---
```python
class User(Model):
    __table__ = "users"
    id: int = fields.PrimaryKey()
    name: str
    email: str = fields.String(unique=True)
    age: int | None = None
    settings: dict = fields.Json(default_factory=dict)
    created_at: datetime = fields.DateTime(default_now=True)

shiba.set_default_connection(cx)

User.create_table()
user = User(name="John", email="j@x.com")
user.save()              # INSERT, hidrata pk con LAST_INSERT_ID
User.find(1)             # → User | None
User.where("age", ">", 18).order_by("created_at", "DESC").get()  # → list[User]
```

Componentes
- `shiba/orm/fields.py` — `Field` + subclases semánticas
  (PrimaryKey, String, Integer, BigInteger, Boolean, Text, Json,
  DateTime, DateField, DecimalField, Enum, ForeignKey) y motor de
  inferencia desde anotaciones (`int | None` → nullable, `dict` → JSON).
- `shiba/orm/model.py` — `Model` con metaclass que lee anotaciones por
  MRO usando `inspect.get_annotations(eval_str=True)` para soportar
  `from __future__ import annotations`. `ModelQuery[T]` hidrata las
  filas del builder a instancias del modelo. Conexión global vía
  `set_default_connection()` o override por clase con `__db__`.

Cambios menores
- `ErrorCode.raise_` se tipa como `NoReturn`, eliminando varias
  estructuras unreachable que mypy no entendía.
- Re-exports en `shiba/__init__.py`: `Model`, `fields`,
  `set_default_connection`.

Tests
- 16 tests nuevos cubren inferencia de tipos, save (INSERT/UPDATE),
  delete, find, where con hidratación, JSON roundtrip, create_table.
- Total: 100/100. Lint y mypy estricto verde.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ShibaRoPinoo ShibaRoPinoo merged commit 6a5fd09 into feature/fase-1-query-rico May 18, 2026
0 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant