Files
core/orm/base.py

74 lines
3.2 KiB
Python
Raw Normal View History

2025-07-25 01:04:15 +03:00
import builtins
import logging
2025-07-31 18:55:59 +03:00
from typing import Any, Type
2025-07-25 01:04:15 +03:00
import orjson
2025-07-31 18:55:59 +03:00
from sqlalchemy import JSON
from sqlalchemy.orm import DeclarativeBase
2025-07-25 01:04:15 +03:00
logger = logging.getLogger(__name__)
# Глобальный реестр моделей
REGISTRY: dict[str, Type[Any]] = {}
# Список полей для фильтрации при сериализации
FILTERED_FIELDS: list[str] = []
2025-07-31 18:55:59 +03:00
class BaseModel(DeclarativeBase):
2025-07-25 01:04:15 +03:00
"""
2025-07-31 18:55:59 +03:00
Базовая модель с методами сериализации и обновления
2025-07-25 01:04:15 +03:00
"""
def __init_subclass__(cls, **kwargs: Any) -> None:
REGISTRY[cls.__name__] = cls
super().__init_subclass__(**kwargs)
def dict(self, access: bool = False) -> builtins.dict[str, Any]:
"""
Конвертирует ORM объект в словарь.
Пропускает атрибуты, которые отсутствуют в объекте, но присутствуют в колонках таблицы.
Преобразует JSON поля в словари.
Returns:
Dict[str, Any]: Словарь с атрибутами объекта
"""
column_names = filter(lambda x: x not in FILTERED_FIELDS, self.__table__.columns.keys())
data: builtins.dict[str, Any] = {}
2025-07-31 18:55:59 +03:00
logger.debug(f"Converting object to dictionary {'with access' if access else 'without access'}")
2025-07-25 01:04:15 +03:00
try:
for column_name in column_names:
try:
# Проверяем, существует ли атрибут в объекте
if hasattr(self, column_name):
value = getattr(self, column_name)
# Проверяем, является ли значение JSON и декодируем его при необходимости
if isinstance(value, (str, bytes)) and isinstance(
self.__table__.columns[column_name].type, JSON
):
try:
data[column_name] = orjson.loads(value)
except (TypeError, orjson.JSONDecodeError) as e:
2025-07-31 18:55:59 +03:00
logger.warning(f"Error decoding JSON for column '{column_name}': {e}")
2025-07-25 01:04:15 +03:00
data[column_name] = value
else:
data[column_name] = value
else:
# Пропускаем атрибут, если его нет в объекте (может быть добавлен после миграции)
logger.debug(f"Skipping missing attribute '{column_name}' for {self.__class__.__name__}")
except AttributeError as e:
logger.warning(f"Attribute error for column '{column_name}': {e}")
except Exception as e:
2025-07-31 18:55:59 +03:00
logger.warning(f"Error occurred while converting object to dictionary {e}")
2025-07-25 01:04:15 +03:00
return data
def update(self, values: builtins.dict[str, Any]) -> None:
for key, value in values.items():
if hasattr(self, key):
setattr(self, key, value)
2025-07-31 18:55:59 +03:00
# Alias for backward compatibility
Base = BaseModel