from __future__ import annotations from typing import Any, Dict, Mapping, Tuple from common.orm.fields.PrimaryKeyField import PrimaryKeyField from common.type_checkers.Checkers import chk_none from ..backend._Backend import _Backend from ..fields.Field import Field from .Tools import NoDupOrderedDict class MetaModel(type): @classmethod def __prepare__(metacls, name : str, bases : Tuple[type, ...], **attrs : Any) -> Mapping[str, Any]: return NoDupOrderedDict() def __new__(metacls, name : str, bases : Tuple[type, ...], attrs : NoDupOrderedDict[str, Any]): field_names = list() primary_key_field = None for key, value in attrs.items(): if not isinstance(value, Field): continue attrs[key].name = key field_names.append(key) if isinstance(value, PrimaryKeyField): if primary_key_field is None: primary_key_field = value continue raise AttributeError('PrimaryKey for Model({:s}) already set to attribute({:s})'.format( str(name), str(primary_key_field.name))) cls_obj = super().__new__(metacls, name, bases, dict(attrs)) setattr(cls_obj, '_primary_key_field', primary_key_field) setattr(cls_obj, '_field_names_list', field_names) setattr(cls_obj, '_field_names_set', set(field_names)) return cls_obj class Model(metaclass=MetaModel): def __init__(self, parent : 'Model', primary_key : Any = None) -> None: if not isinstance(parent, Model): str_class_path = '{}.{}'.format(Model.__module__, Model.__name__) raise AttributeError('parent must inherit from {}'.format(str_class_path)) self._parent = parent self._backend = self._parent.backend self._class_name = type(self).__name__ self._backend_key = '{:s}/{:s}'.format(self.parent.backend_key, self._class_name) if self._primary_key_field is not None: # pylint: disable=no-member primary_key_field_name = self._primary_key_field.name # pylint: disable=no-member print('primary_key_field_name', primary_key_field_name) setattr(self, primary_key_field_name, primary_key) self._backend_key += '[{:s}]'.format(getattr(self, primary_key_field_name)) else: try: chk_none('primary_key', primary_key) except: msg = 'Unable to set primary_key({:s}) since no PrimaryKeyField is defined in the model' raise AttributeError(msg.format(str(primary_key))) @property def parent(self) -> 'Model': return self._parent @property def backend(self) -> _Backend: return self._parent.backend @property def backend_key(self) -> str: return self._backend_key def load(self) -> None: attributes = self._backend.dict_get(self._backend_key).items() for name in self._field_names_list: # pylint: disable=no-member if name not in attributes: continue setattr(self, name, attributes[name]) def save(self) -> None: attributes : Dict[str, Any] = { name:repr(getattr(self, name)) for name in self._field_names_list # pylint: disable=no-member } self._backend.dict_update(self._backend_key, attributes) def delete(self) -> None: self._backend.dict_delete(self._backend_key) def dump_id(self) -> Dict: raise NotImplementedError() def dump(self) -> Dict: raise NotImplementedError() def __repr__(self) -> str: pk_field = self._primary_key_field # pylint: disable=no-member pk_field_name = None if pk_field is None else pk_field.name # pylint: disable=no-member arguments = ', '.join( '{:s}={:s}{:s}'.format( name, repr(getattr(self, name)), '(PK)' if name == pk_field_name else '') for name in self._field_names_list # pylint: disable=no-member ) return '{:s}({:s})'.format(self._class_name, arguments)