Первый коммит
This commit is contained in:
115
database/database.py
Normal file
115
database/database.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Управление 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
|
||||
Reference in New Issue
Block a user