Files
PrimoGuardBot-/database/database.py
2026-02-17 11:24:55 +07:00

116 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Управление SQLAlchemy движком и сессиями.
"""
from pathlib import Path
from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import (
create_async_engine,
async_sessionmaker,
AsyncSession,
AsyncEngine
)
from middleware.loggers import logger
from .models import Base
__all__ = ("Database", "get_db")
class Database:
"""
Менеджер SQLAlchemy базы данных.
Attributes:
engine: Async движок SQLAlchemy
session_factory: Фабрика сессий
"""
def __init__(self, db_path: str = "banwords.db"):
"""
Args:
db_path: Путь к SQLite файлу
"""
# Создаём директорию если не существует
db_file = Path(db_path)
db_file.parent.mkdir(parents=True, exist_ok=True)
# SQLite URL для async
db_url = f"sqlite+aiosqlite:///{db_path}"
# Создаём async движок
self.engine: AsyncEngine = create_async_engine(
db_url,
echo=False, # Логирование SQL запросов (False для прода)
future=True,
pool_pre_ping=True, # Проверка соединения
)
# Фабрика сессий
self.session_factory = async_sessionmaker(
self.engine,
class_=AsyncSession,
expire_on_commit=False,
)
logger.info(
f"SQLAlchemy инициализирован: {db_path}",
log_type="DATABASE"
)
async def init(self) -> None:
"""Создаёт все таблицы в БД"""
try:
async with self.engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
logger.info(
"Таблицы базы данных созданы",
log_type="DATABASE"
)
except Exception as e:
logger.error(
f"Ошибка создания таблиц: {e}",
log_type="DATABASE"
)
raise
async def close(self) -> None:
"""Закрывает соединения с БД"""
await self.engine.dispose()
logger.info("База данных закрыта", log_type="DATABASE")
def get_session(self) -> AsyncGenerator[AsyncSession, None]:
"""
Создаёт новую сессию (контекстный менеджер).
Usage:
async with db.get_session() as session:
result = await session.execute(select(BanWord))
words = result.scalars().all()
Yields:
AsyncSession: Сессия для работы с БД
"""
return self.session_factory()
# Глобальный экземпляр
_db_instance: Database | None = None
def get_db(db_path: str = "banwords.db") -> Database:
"""
Возвращает глобальный экземпляр Database (Singleton).
Args:
db_path: Путь к БД (используется только при первом вызове)
Returns:
Database: Экземпляр базы данных
"""
global _db_instance
if _db_instance is None:
_db_instance = Database(db_path)
return _db_instance