Files
sakhteman/core/service/mysql_orm.py
T
2026-02-17 22:37:43 +03:30

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