Skip to content

Commit

Permalink
add: mixins for models
Browse files Browse the repository at this point in the history
* mv DatetimeModel ti mixins.py
* add ReprMixin, SerializeMixin, UserRelationshipMixin
  • Loading branch information
NotBupyc committed Oct 3, 2024
1 parent 48052ee commit f771dd7
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 19 deletions.
25 changes: 6 additions & 19 deletions bot/database/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,18 @@
# Imports
from __future__ import annotations

from datetime import datetime
from typing import Any

from sqlalchemy import BigInteger, Column, DateTime, func, Integer
from sqlalchemy.orm import declarative_base, DeclarativeBase
from sqlalchemy import BigInteger
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Mapped, mapped_column

from bot.database.models.mixins import ReprMixin, SerializeMixin

Base = declarative_base()


class BaseModel(Base):
class BaseModel(Base, ReprMixin, SerializeMixin):
__abstract__ = True

id: Mapped[int] = mapped_column(Integer, unique=True, autoincrement=True, nullable=False, primary_key=True)

@property
def to_dict(self) -> dict[str, Any]:
return {c.name: getattr(self, c.name) for c in self.__table__.columns}

def __repr__(self) -> str:
return f"<{self.__class__.__name__}(id={self.id!r})>"


class DatetimeModel(Base):
__abstract__ = True
id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False, primary_key=True)

created_at: Mapped[datetime] = mapped_column(DateTime(), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(), server_onupdate=func.now(), nullable=True)
114 changes: 114 additions & 0 deletions bot/database/models/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING

from sqlalchemy import func, DateTime, BigInteger, ForeignKey
from sqlalchemy.orm import mapped_column, Mapped, declared_attr, relationship

if TYPE_CHECKING:
from .user import User

class ReprMixin:
__repr_max_length = 25
__repr_attrs = []

def _repr_attrs_str(self):
max_length = self.__repr_max_length

values = []
for key in self.__repr_attrs:
if not hasattr(self, key):
raise KeyError("{} has incorrect attribute '{}' in "
"__repr__attrs__".format(self.__class__, key))
value = getattr(self, key)

value = str(value)
if len(value) > max_length:
value = value[:max_length] + "..."
values.append("{}={}".format(key, value))

return " ".join(values)

def __repr__(self):
return "<{} {}>".format(self.__class__.__name__,
self._repr_attrs_str()
if self._repr_attrs_str else "")

class SerializeMixin:
def to_dict(
self,
ignored_columns: list | None = None,
relationships: bool = False
) -> dict:
if ignored_columns is None:
ignored_columns = []
result: dict = {}

for c in self.__table__.columns:
if c.name in ignored_columns:
continue
result[c.name] = getattr(self, c.name)

if relationships:
for relationship_name in self.__mapper__.relationships.keys():
try:
relationship_value = getattr(self, relationship_name)
except Exception:
continue

if isinstance(relationship_value, list):
result[relationship_name] = [
item.to_dict()
for item in relationship_value
]
else:
result[relationship_name] = (
relationship_value.to_dict()
if relationship_value is not None
else None
)
return result

class DateTimeMixin:
__datetime_func = func.now()
__datetime_timezone: bool = False

@declared_attr
def created_at(cls) -> Mapped[datetime]:
return mapped_column(
DateTime(timezone=cls.__datetime_timezone),
server_default=cls.__datetime_func,
)

@declared_attr
def updated_at(cls) -> Mapped[datetime]:
return mapped_column(
DateTime(timezone=cls.__datetime_timezone),
server_onupdate=cls.__datetime_func
)


class UserRelationshipMixin:
_user_id_nullable: bool = False
_user_id_unique: bool = False
_user_back_populates: str | None = None
_user_relationship_kwargs: dict = {}

@declared_attr
def user_id(cls) -> Mapped[int]:
return mapped_column(
BigInteger,
ForeignKey("users.id"),
unique=cls._user_id_unique,
nullable=cls._user_id_nullable
)

@declared_attr
def user(cls) -> Mapped["User"]:
return relationship(
"User",
back_populates=cls._user_back_populates,
**cls._user_relationship_kwargs
)

0 comments on commit f771dd7

Please sign in to comment.