from datetime import datetime
from typing import Any, Dict, Optional, TypeVar
from uuid import UUID as PythonUUID
from uuid import uuid4
from sqlalchemy import DateTime, String, text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from nomy_data_models.utils.string import to_snake_case
T = TypeVar("T", bound="BaseModel")
class Base(DeclarativeBase):
def dict(self) -> Dict[str, Any]:
return {
column.name: getattr(self, column.name) for column in self.__table__.columns
}
def update_from_dict(self, data: Dict[str, Any]) -> None:
for key, value in data.items():
if hasattr(self, key):
setattr(self, key, value)
class BaseModel(Base):
__abstract__ = True
@declared_attr.directive
@classmethod
def __tablename__(cls) -> str:
return to_snake_case(cls.__name__)
id: Mapped[PythonUUID] = mapped_column(
UUID, primary_key=True, default=uuid4, nullable=False
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=text("CURRENT_TIMESTAMP"),
nullable=False,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=text("CURRENT_TIMESTAMP"),
onupdate=text("CURRENT_TIMESTAMP"),
nullable=False,
)
created_by: Mapped[Optional[str]] = mapped_column(String(length=255), nullable=True)
updated_by: Mapped[Optional[str]] = mapped_column(String(length=255), nullable=True)
def __repr__(self) -> str:
return f"<{self.__class__.__name__}(id={self.id})>"
def to_dict(self) -> Dict[str, Any]:
return {
column.name: getattr(self, column.name) for column in self.__table__.columns
}
@classmethod
def from_dict(cls: type[T], data: Dict[str, Any]) -> T:
return cls(**{k: v for k, v in data.items() if hasattr(cls, k)})