Source code for joop.dao
"""Data Access Object (DAO) module for joop.
This module provides abstract classes for creating Data Access Objects (DAOs) to manage
Pydantic and SQLModel-based models. DAOs provide a layer of abstraction between the
representation of the object (especially at the storage layer) and the business logic
code that uses or manipulates the object.
Classes:
DAO:
An abstract wrapper for a Pydantic model or any class derived from it.
SQLDAO:
An abstract class for SQL models, extending the DAO class.
"""
from typing import List, Type, Optional
from dataclasses import dataclass
import pydantic
import sqlmodel
[docs]
class DAO():
"""
An abstract wrapper for a Pydantic model or any class derived from it.
The DAO class provides methods to interact with the underlying model, including
converting it to a dictionary and retrieving its fields. It is typically constructed
around an existing model using the `from_model` method.
Attributes:
_modeltype (Type): The type of the model, defaulting to `pydantic.BaseModel`.
Properties:
model (pydantic.BaseModel): The underlying Pydantic model instance.
Methods:
from_model(model: pydantic.BaseModel) -> 'DAO':
Creates a DAO instance from a given Pydantic model.
to_dict() -> dict:
Converts the underlying model to a dictionary using aliases for field names.
get_model_fields() -> List[str]:
Retrieves the names of all fields defined in the `_modeltype`.
"""
_modeltype : Type = pydantic.BaseModel
@property
def model(self) -> pydantic.BaseModel:
return self._model
@model.setter
def model(self, value : pydantic.BaseModel):
self._model = value
[docs]
@classmethod
def from_model(cls, model : pydantic.BaseModel) -> 'DAO':
if model is None:
return None
if isinstance(type(model), cls._modeltype):
raise ("Invalid Model Supplied")
res = cls()
res._model = model
return res
[docs]
def to_dict(self) -> dict:
"""
Convert the underlying model to a dictionary, using aliases for field names.
Returns:
dict: A dictionary representation of the model with aliases as keys.
Raises:
ValueError: If no model is set for this DAO instance.
TypeError: If the model does not have Pydantic fields.
"""
if not hasattr(self, '_model') or self._model is None:
raise ValueError("No model is set for this DAO instance.")
# Ensure the model has the necessary fields and aliases
if not hasattr(self._model, '__fields__'):
raise TypeError("The model does not have Pydantic fields.")
# Use SQLModel's/Pydantic basemodel's dict() method to get the base dictionary
base_dict = self._model.dict()
# Build the dictionary using aliases
result = {}
for field_name, field in self._model.__fields__.items():
alias = field.alias or field_name # Use alias if defined, otherwise fallback to field name
result[alias] = base_dict[field_name]
return result
[docs]
@classmethod
def get_model_fields(cls) -> List[str]:
"""
Get the names of all fields defined in the `_modeltype`.
Returns:
List[str]: A list of field names.
Raises:
TypeError: If `_modeltype` is not a subclass of `pydantic.BaseModel`.
"""
if not issubclass(cls._modeltype, pydantic.BaseModel):
raise TypeError("_modeltype must be a subclass of pydantic.BaseModel")
res = [field.alias if field.alias else name for name, field in cls._modeltype.__fields__.items()]
return res
[docs]
class SQLDAO(DAO):
"""
An abstract class for SQL models, extending the DAO class.
The SQLDAO class provides additional methods for interacting with SQLModel-based
models, such as retrieving all records from the database.
Attributes:
_modeltype (Type): The type of the model, defaulting to `sqlmodel.SQLModel`.
Methods:
get_all(session: sqlmodel.Session) -> List['SQLDAO']:
Retrieves all records from the database for the given model type and returns
them as a list of SQLDAO instances.
"""
_modeltype : Type = sqlmodel.SQLModel
[docs]
@classmethod
def get_all(cls, session: sqlmodel.Session) -> List['SQLDAO']:
"""
Retrieve all records from the database for the given model type and return them as a list of SQLDAO instances.
Args:
session (sqlmodel.Session): The database session to use for the query.
Returns:
List[SQLDAO]: A list of SQLDAO instances for the model type.
Raises:
TypeError: If `_modeltype` is not a subclass of `sqlmodel.SQLModel`.
"""
if not issubclass(cls._modeltype, sqlmodel.SQLModel):
raise TypeError("_modeltype must be a subclass of sqlmodel.SQLModel")
db_results = session.query(cls._modeltype).all()
return [cls.from_model(result) for result in db_results]