if isinstance(id_, int) and id_ < 1:
msg = f'illegal {self.__class__.__name__} ID, must be >=1: {id_}'
raise HandledException(msg)
+ if isinstance(id_, str) and "" == id_:
+ msg = f'illegal {self.__class__.__name__} ID, must be non-empty'
+ raise HandledException(msg)
self.id_ = id_
+ def __hash__(self) -> int:
+ hashable = [self.id_] + [getattr(self, name) for name in self.to_save]
+ for definition in self.to_save_relations:
+ attr = getattr(self, definition[2])
+ hashable += [tuple(rel.id_ for rel in attr)]
+ for name in self.to_save_versioned:
+ hashable += [hash(getattr(self, name))]
+ return hash(tuple(hashable))
+
def __eq__(self, other: object) -> bool:
if not isinstance(other, self.__class__):
return False
- to_hash_me = tuple([self.id_] +
- [getattr(self, name) for name in self.to_save])
- to_hash_other = tuple([other.id_] +
- [getattr(other, name) for name in other.to_save])
- return hash(to_hash_me) == hash(to_hash_other)
+ return hash(self) == hash(other)
def __lt__(self, other: Any) -> bool:
if not isinstance(other, self.__class__):
assert isinstance(other.id_, int)
return self.id_ < other.id_
+ # cache management
+
@classmethod
- def get_cached(cls: type[BaseModelInstance],
- id_: BaseModelId) -> BaseModelInstance | None:
+ def _get_cached(cls: type[BaseModelInstance],
+ id_: BaseModelId) -> BaseModelInstance | None:
"""Get object of id_ from class's cache, or None if not found."""
# pylint: disable=consider-iterating-dictionary
cache = cls.get_cache()
cache = self.__class__.get_cache()
del cache[self.id_]
+ # object retrieval and generation
+
@classmethod
def from_table_row(cls: type[BaseModelInstance],
# pylint: disable=unused-argument
"""
obj = None
if id_ is not None:
- obj = cls.get_cached(id_)
+ obj = cls._get_cached(id_)
if not obj:
for row in db_conn.row_where(cls.table_name, 'id', id_):
obj = cls.from_table_row(db_conn, row)
return filtered
return items
+ # database writing
+
def save(self, db_conn: DatabaseConnection) -> None:
"""Write self to DB and cache and ensure .id_.
def remove(self, db_conn: DatabaseConnection) -> None:
"""Remove from DB and cache, including dependencies."""
- if self.id_ is None or self.__class__.get_cached(self.id_) is None:
+ # pylint: disable=protected-access
+ # (since we remain within class)
+ if self.id_ is None or self.__class__._get_cached(self.id_) is None:
raise HandledException('cannot remove unsaved item')
for attr_name in self.to_save_versioned:
getattr(self, attr_name).remove(db_conn)