import logging from sqlalchemy import inspect from sqlmodel import Session, SQLModel, create_engine from app.services.settings import settings logger = logging.getLogger(__name__) # the lifespan startup (create_db_and_tables, create_first_user), # get_db_session(), the module level scanner session and # the get_session() FastAPI dependency resolve their engine # through get_engine(). That makes configure_engine() the single # injection point for tests _engine = None _engine_is_default = False def _build_engine_from_settings(): return create_engine( settings.sqlalchemy_database_url, connect_args={"check_same_thread": False} ) def get_engine(): """Return the engine, building it from settings on first use.""" global _engine, _engine_is_default if _engine is None: _engine = _build_engine_from_settings() _engine_is_default = True return _engine def configure_engine(engine) -> None: """Install *engine* as the engine used by this module from now on. The replaced engine is not closed because the scanner (or other long-lived session) could still hold connections """ global _engine, _engine_is_default _engine = engine _engine_is_default = False def reset_engine() -> None: """Drop a configured engine and rebuild from settings on the next call. Only an engine this module built itself is closed. """ global _engine, _engine_is_default if _engine is not None and _engine_is_default: _engine.dispose() _engine = None _engine_is_default = False def create_db_and_tables(): engine = get_engine() existing_tables = inspect(engine).get_table_names() if not existing_tables: SQLModel.metadata.create_all(engine) from alembic.config import Config from alembic import command alembic_cfg = Config(settings.alembic_config) alembic_cfg.set_main_option("sqlalchemy.url", str(engine.url)) alembic_cfg.attributes["sqlalchemy.url"] = settings.sqlalchemy_database_url command.stamp(alembic_cfg, "head") logger.info("Database created and tables initialized.") else: logger.info( "Database already initialized (%d tables found).", len(existing_tables) ) def get_session(): with Session(get_engine()) as db: yield db def get_db_session(): return Session(get_engine()) def add_and_refresh(db: Session, obj): db.add(obj) db.commit() db.refresh(obj) return obj