""" SQLAlchemy async setup برای MySQL (فقط خواندنی). از asyncmy به عنوان async driver استفاده می‌کند. """ from contextlib import asynccontextmanager from typing import Optional from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import declarative_base, sessionmaker from core.utils.mysql import get_mysql_config # Base class برای تمام مدل‌های ORM Base = declarative_base() _engine = None _async_session_maker = None def get_mysql_url(connection_name: str = None) -> str: """ساخت URL برای SQLAlchemy از تنظیمات MySQL""" cfg = get_mysql_config(connection_name) return f"mysql+asyncmy://{cfg['user']}:{cfg['password']}@{cfg['host']}:{cfg['port']}/{cfg['db']}?charset=utf8mb4" def get_async_engine(connection_name: str = None): """برگرداندن async engine (در اولین فراخوانی ساخته می‌شود)""" global _engine if _engine is None: url = get_mysql_url(connection_name) _engine = create_async_engine( url, echo=False, # برای debug می‌تونی True کنی pool_pre_ping=True, # چک کردن کانکشن قبل از استفاده pool_size=5, max_overflow=10, ) return _engine def get_async_session_maker(connection_name: str = None): """برگرداندن async sessionmaker""" global _async_session_maker if _async_session_maker is None: engine = get_async_engine(connection_name) _async_session_maker = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False, autocommit=False, autoflush=False, ) return _async_session_maker @asynccontextmanager async def get_mysql_session(connection_name: str = None): """ Context manager برای گرفتن async session (فقط خواندنی). استفاده: async with get_mysql_session() as session: result = await session.execute(select(User).where(User.id == 1)) user = result.scalar_one_or_none() """ session_maker = get_async_session_maker(connection_name) async with session_maker() as session: # تنظیم session به حالت فقط-خواندنی await session.execute("SET SESSION TRANSACTION READ ONLY") try: yield session finally: await session.rollback() # برای read-only همیشه rollback می‌کنیم async def close_mysql_engine(): """بستن engine (مثلاً در shutdown اپ).""" global _engine, _async_session_maker if _engine is not None: await _engine.dispose() _engine = None _async_session_maker = None