| # -*- coding: utf-8 -*- | |
| from sqlalchemy import create_engine | |
| from sqlalchemy.orm import sessionmaker, declarative_base | |
| import os | |
| from dotenv import load_dotenv | |
| import importlib | |
| # 🔒 Caminho absoluto do projeto | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| # Carrega variáveis de ambiente (.env) antes de ler DATABASE_URL | |
| load_dotenv() | |
| # ============================================================ | |
| # 🔀 SUPORTE A DOIS BANCOS (Produção/Teste) COM FALLBACK | |
| # ============================================================ | |
| # Tentamos usar o roteador (db_router.py). Se não existir ainda, | |
| # caímos no comportamento original usando apenas DATABASE_URL. | |
| try: | |
| from db_router import ( | |
| get_engine as _router_get_engine, | |
| get_session_factory as _router_get_session_factory, | |
| SessionLocal as _router_SessionLocal, | |
| ) | |
| _HAS_ROUTER = True | |
| except Exception: | |
| _HAS_ROUTER = False | |
| # 🔧 Fallback: mesma lógica do seu módulo original — um único DATABASE_URL | |
| DATABASE_URL = os.getenv( | |
| "DATABASE_URL", | |
| f"sqlite:///{os.path.join(BASE_DIR, 'load.db')}" | |
| ) | |
| engine_args = { | |
| "echo": False, | |
| "pool_pre_ping": True, | |
| } | |
| # Parâmetros específicos para SQLite (apenas se o fallback estiver ativo) | |
| if DATABASE_URL.startswith("sqlite"): | |
| engine_args["connect_args"] = {"check_same_thread": False} | |
| # ============================================================ | |
| # Engine / SessionLocal (com ou sem roteador) | |
| # ============================================================ | |
| if _HAS_ROUTER: | |
| # ✅ Usa engine e SessionLocal do banco ATIVO (Produção/Teste), conforme escolha no login | |
| def get_engine(): | |
| return _router_get_engine() | |
| def _session_factory(): | |
| return _router_get_session_factory() | |
| # A SessionLocal do roteador já entrega sessões no banco ativo | |
| SessionLocal = _router_SessionLocal | |
| else: | |
| # ✅ Fallback: comportamento original com DATABASE_URL único | |
| _engine = create_engine(DATABASE_URL, **engine_args) | |
| def get_engine(): | |
| return _engine | |
| _SessionFactory = sessionmaker( | |
| autocommit=False, | |
| autoflush=False, | |
| bind=_engine, | |
| ) | |
| def _session_factory(): | |
| return _SessionFactory | |
| # Compatível com seu uso atual: SessionLocal() -> sessão | |
| SessionLocal = _SessionFactory | |
| # ⚠️ Compatibilidade: expõe 'engine' resolvendo via get_engine() | |
| # Observação importante: | |
| # - Se trocar o banco após a importação deste módulo (via login), | |
| # prefira sempre chamar get_engine() ou criar sessões com SessionLocal(), | |
| # pois 'engine' abaixo é resolvido apenas uma vez (na importação). | |
| engine = get_engine() | |
| # ORM Base | |
| Base = declarative_base() | |
| # ============================================================ | |
| # 🛠️ Utilitários (opcionais) | |
| # ============================================================ | |
| def init_schema(): | |
| """ | |
| Cria/atualiza as tabelas no banco ATIVO. | |
| • Com roteador: aplica no banco escolhido (Produção/Teste). | |
| • Sem roteador: aplica no DATABASE_URL padrão. | |
| Use em DEV/TESTE; em produção, prefira migrações (ex.: Alembic). | |
| """ | |
| # Importa 'models' de forma tardia e segura (sem wildcard) para registrar todos os mapeamentos | |
| # antes de criar as tabelas. Isso evita import circular no topo. | |
| try: | |
| importlib.import_module("models") | |
| except ModuleNotFoundError: | |
| # Se seus modelos estiverem em outro pacote/caminho, ajuste aqui: | |
| # importlib.import_module("app.models") # exemplo | |
| raise | |
| Base.metadata.create_all(bind=get_engine()) | |
| def db_info() -> dict: | |
| """ | |
| Retorna informações básicas do banco ativo (para debug/UX). | |
| """ | |
| eng = get_engine() | |
| try: | |
| url = str(eng.url) | |
| except Exception: | |
| url = DATABASE_URL | |
| return { | |
| "url": url, | |
| "using_router": _HAS_ROUTER, | |
| } | |