Implement BaseModel class, TimestampMixin, TzDateTime type; cleanup Task model

This commit is contained in:
Lexi / Zoe 2022-04-15 21:45:10 +02:00
parent 7ce43a2bfe
commit 252c15acbd
Signed by: binaryDiv
GPG key ID: F8D4956E224DA232
15 changed files with 223 additions and 42 deletions

View file

@ -1,5 +1,5 @@
# Base model first
from .base import metadata, BaseModel
from .base import BaseModel
# Data models
from .task import Task

View file

@ -1,22 +1,62 @@
from sqlalchemy import MetaData
from sqlalchemy.orm import declarative_base
from typing import Any, Iterable, Optional
from sqlalchemy import Column, Integer, inspect
from sqlalchemy.orm import InstanceState, as_declarative
from tofu_api.common.database import Col, MetaData
__all__ = [
'metadata',
'BaseModel',
]
# Define naming convention for constraints
_naming_convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}
# Create metadata object for database schemas
metadata = MetaData(naming_convention=_naming_convention)
@as_declarative(name='BaseModel', metadata=MetaData())
class BaseModel:
"""
Declarative base class for database models.
"""
# Generate declarative base class for database models
BaseModel = declarative_base(name='BaseModel', metadata=metadata)
# Default primary key
id: Col[int] = Column(Integer, nullable=False, primary_key=True)
def __repr__(self) -> str:
"""
Return a string representation of this object.
"""
return self._repr(id=self.id) if hasattr(self, 'id') else self._repr()
def _repr(self, **fields) -> str:
"""
Helper method for implementing __repr__.
"""
state: InstanceState = inspect(self)
state_str = f' [transient {id(self)}]' if state.transient \
else f' [pending {id(self)}]' if state.pending \
else ' [deleted]' if state.deleted \
else ' [detached]' if state.detached else ''
param_str = ', '.join([f'{key}={value!r}' for key, value in fields.items()] if fields else state.identity or [])
return f'<{type(self).__name__}({param_str}){state_str}>'
def to_dict(
self,
*,
fields: Optional[Iterable[str]] = None,
exclude: Optional[Iterable[str]] = None,
) -> dict[str, Any]:
"""
Return the object's data as a dictionary.
By default, the dictionary will contain all table columns (with their column name as key) defined in the model. This can be
overridden by setting the `fields` and/or `exclude` parameters, in which case only fields that are listed in `fields` will be
included in the dictionary, except for fields listed in `exclude`.
"""
# Determine fields to include in dictionary (starting will all table columns)
included_fields = set(column.name for column in self.__table__.columns)
if fields is not None:
included_fields.intersection_update(fields)
if exclude is not None:
included_fields.difference_update(exclude)
return {
field: getattr(self, field) for field in included_fields
}

View file

@ -1,25 +1,19 @@
from sqlalchemy import Column, Integer, String, Text
from sqlalchemy import Column, String, Text
from tofu_api.common.database import Col
from tofu_api.common.database.mixins import TimestampMixin
from .base import BaseModel
class Task(BaseModel):
class Task(TimestampMixin, BaseModel):
"""
Database model for tasks.
"""
__tablename__ = 'tasks'
__tablename__ = 'task'
id: int = Column(Integer, nullable=False, primary_key=True)
# TODO: created_at, modified_at
title: Col[str] = Column(String(255), nullable=False)
description: Col[str] = Column(Text, nullable=False, default='')
title: str = Column(String(255), nullable=False)
description: str = Column(Text, nullable=False, default='')
def to_dict(self) -> dict:
# TODO: Implement a generic to_dict() in the base model
return {
'id': self.id,
'title': self.title,
'description': self.description,
}
def __repr__(self):
return self._repr(id=self.id, title=self.title)