82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
"""
|
|
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
|