from __future__ import annotations from collections.abc import Generator from contextlib import contextmanager from sqlalchemy import MetaData, create_engine, event, text from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker from app.core.config import settings NAMING_CONVENTION = { "ix": "ix_%(column_0_label)s", "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s", } metadata = MetaData( naming_convention=NAMING_CONVENTION, schema=settings.db_schema, ) engine = create_engine( settings.database_url, pool_size=settings.db_pool_size, max_overflow=settings.db_max_overflow, echo=settings.db_echo, pool_pre_ping=True, pool_recycle=3600, connect_args={ "options": f"-c search_path={settings.db_schema},public" }, ) @event.listens_for(engine, "connect") def set_search_path(dbapi_connection: object, connection_record: object) -> None: cursor = dbapi_connection.cursor() # type: ignore[union-attr] cursor.execute(f"SET search_path TO {settings.db_schema}, public") cursor.close() dbapi_connection.commit() # type: ignore[union-attr] SessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=engine, ) class Base(DeclarativeBase): """Base class for all SQLAlchemy models.""" metadata = metadata def get_db() -> Generator[Session, None, None]: """Dependency to get database session.""" db = SessionLocal() try: yield db finally: db.close() @contextmanager def get_db_context() -> Generator[Session, None, None]: """Context manager for database session (used outside request scope).""" db = SessionLocal() try: yield db db.commit() except Exception: db.rollback() raise finally: db.close() def check_database_connection() -> bool: """Verify database connectivity.""" try: with engine.connect() as conn: conn.execute(text("SELECT 1")) return True except Exception: return False