"""A file containing the implementation of the Model class for database management"""
import copy
import uuid
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar
from .core import LightDB
from .exceptions import ValidationError, NoArgsProvidedError
from .fields import Field
from .query import Query
MODEL = TypeVar("MODEL", bound="Model")
[docs]
class Model(metaclass=ModelMeta):
"""A base model class that provides a simple interface for interacting with data in a LightDB database"""
__table__: str = None
__db__: LightDB = None
def __init__(self, **kwargs) -> None:
"""Initializes a new instance of the model with the provided keyword arguments
Params:
kwargs (``Dict[str, Any]``): Keyword arguments representing field names and values for the model instance
"""
self._fields_map: Dict[str, Field] = copy.deepcopy(self._fields_map)
if "_id" not in kwargs:
kwargs["_id"] = str(uuid.uuid4())
for name, field in self._fields_map.items():
value = kwargs.get(name, field.value if field.value is not None else field.default)
field.value = value
field.validate()
self._fields_map[name] = field
super().__setattr__(name, field.value)
def __setattr__(self, key: str, value: Any) -> None:
if key in self._fields_map:
field = self._fields_map[key]
field.value = value
field.validate()
else:
super().__setattr__(key, value)
def __getattribute__(self, item: Any) -> Any:
fields_map = super().__getattribute__("_fields_map")
if item in fields_map:
value = fields_map[item].value
else:
value = super().__getattribute__(item)
return value
def __str__(self) -> str:
return self.__repr__()
def __repr__(self) -> str:
fields_info = [f"{name}={field.value}" for name, field in self._fields_map.items()]
return f"{self.__class__.__name__}({', '.join(fields_info)})"
[docs]
@classmethod
def create(cls: Type[MODEL], **kwargs) -> MODEL:
"""Creates a new instance of the model with the provided keyword arguments and saves it to the database
Params:
kwargs (``Dict[str, Any]``): Keyword arguments representing field names and values for the model instance
Returns:
``Model``: The newly created instance of the model
"""
if not kwargs:
raise NoArgsProvidedError("No `kwargs` were provided")
instance = cls(**kwargs)
instance.save()
return instance
[docs]
@classmethod
def get(cls: Type[MODEL], *args, **kwargs) -> Optional[MODEL]:
"""Retrieves a single instance of the model that matches the provided filter criteria
Params:
kwargs (``Dict[str, Any]``): Keyword arguments representing filter criteria for the model instance
Returns:
``Optional[Model]``: The matching instance of the model, or None if no matching instance is found
"""
if not (args or kwargs):
raise NoArgsProvidedError("No `args` or `kwargs` were provided")
results = cls.filter(*args, **kwargs)
if not results:
return None
if len(results) > 1:
raise ValueError(f"Multiple instances of `{cls.__name__}` model found by the specified filters")
return results[0]
[docs]
def save(self) -> None:
"""Saves the current state of the model instance to the database"""
existing_instance = self.get(_id=self._fields_map["_id"].value)
if existing_instance:
existing_instance.delete()
new_data = {name: field.value for name, field in self._fields_map.items()}
self.__db__.setdefault(self.__table__, []).append(new_data)
self.__db__.save()
[docs]
def delete(self) -> None:
"""Deletes the current instance of the model from the database"""
rows = self.__db__.get(self.__table__, [])
for item in rows:
if item["_id"] == self._fields_map["_id"].value:
rows.remove(item)
self.__db__.save()
[docs]
@classmethod
def filter(cls: Type[MODEL], *args, **kwargs) -> List[MODEL]:
"""Retrieves a list of instances of the model that matmatch thech the provided filter criteria
Params:
kwargs (``Dict[str, Any]``): Keyword arguments representing filter criteria for the model instances
Returns:
A list of instances of the model that provided filter criteria
"""
if not (args or kwargs):
raise NoArgsProvidedError("No `args` or `kwargs` were provided")
query = Query(cls)
query.where(*args, **kwargs)
return query.execute()
[docs]
@classmethod
def all(cls: Type[MODEL], use_db: LightDB = None) -> List[MODEL]:
"""Retrieves a list of all instances of the model from the database
Returns:
``List[Model]``: A list of all instances of the model
"""
results = []
for row in (use_db or cls.__db__).get(cls.__table__, []):
results.append(cls(**row))
return results