from __future__ import annotations import typing as T from flask import abort from sqlalchemy_serializer import SerializerMixin from pycs import db from pycs.database.util import commit_on_return class BaseModel(db.Model, SerializerMixin): """ Base model class """ __abstract__ = True # setup of the SerializerMixin date_format = '%s' # Unixtimestamp (seconds) datetime_format = '%d. %b. %Y %H:%M:%S' time_format = '%H:%M' id = db.Column(db.Integer, primary_key=True) serialize_only: tuple = ("id", "identifier") def identifier(self): """ alias for id attribute """ return self.id def __repr__(self): attrs = self.serialize() content = ", ".join([f"{attr}={value}" for attr, value in attrs.items()]) return f"<{self.__class__.__name__}: {content}>" def serialize(self) -> dict: """ default model serialization method """ return self.to_dict() @commit_on_return def delete(self) -> dict: """ delete this instance from the database :return: serialized self """ dump = self.serialize() db.session.delete(self) return dump # do an alias remove = delete @classmethod def new(cls, commit: bool = True, **kwargs): """ creates a new object. optionally commits the created object. """ obj = cls(**kwargs) db.session.add(obj) if commit: obj.commit() return obj @classmethod def get_or_create(cls, **kwargs) -> T.Tuple[BaseModel, bool]: """ get an object from the DB based on the kwargs, or create an object with these. """ is_new = False obj = cls.query.filter_by(**kwargs).one_or_none() if obj is None: obj = cls.new(commit=False, **kwargs) is_new = True return obj, is_new @classmethod def get_or_404(cls, obj_id: int) -> BaseModel: """ get an object for the given id or raise 404 error if the object is not present """ obj = cls.query.get(obj_id) if obj is None: abort(404, f"{cls.__name__} with ID {obj_id} could not be found!") return obj @staticmethod def commit(): """ commit current session """ db.session.commit() @staticmethod def flush(): """ flush current session """ db.session.flush() class NamedBaseModel(BaseModel): """ Extends the base model with a name attribute. """ __abstract__ = True name = db.Column(db.String, nullable=False) serialize_only: tuple = BaseModel.serialize_only + ("name",) @commit_on_return def set_name(self, name: str): """ set the name attribute """ self.name = name