Roudrigus commited on
Commit
b14cd8d
·
verified ·
1 Parent(s): 9aabbbe

Update banco.py

Browse files
Files changed (1) hide show
  1. banco.py +62 -36
banco.py CHANGED
@@ -6,10 +6,17 @@ Compatível com:
6
  - Fallback: um único DATABASE_URL vindo de env/Secrets
7
  - Postgres / MySQL / SQLite (c/ alias de case para load.db no Linux)
8
  - Garantia de criação do diretório pai do SQLite (evita 'unable to open database file')
 
 
 
 
9
  """
10
 
11
  import os
12
  import shutil
 
 
 
13
  from sqlalchemy import create_engine
14
  from sqlalchemy.orm import sessionmaker, declarative_base
15
  from dotenv import load_dotenv
@@ -59,10 +66,8 @@ def _ensure_parent_dir_sqlite(sqlite_url: str) -> str:
59
  if not sqlite_url or not sqlite_url.startswith("sqlite"):
60
  return sqlite_url
61
 
62
- # Extrai caminho do arquivo a partir da URL
63
  # Formatos: sqlite:///relativo.db | sqlite:////abs/path/to.db
64
  path = sqlite_url.replace("sqlite:///", "", 1)
65
- # se vier com // (pouco comum), normaliza
66
  if path.startswith("//"):
67
  path = path[1:]
68
  file_path = os.path.abspath(path)
@@ -80,19 +85,24 @@ def _ensure_parent_dir_sqlite(sqlite_url: str) -> str:
80
 
81
 
82
  # =========================================================
83
- # 3) Suporte a roteador (db_router.py) — opcional
84
  # =========================================================
85
- # Se existir um roteador, delegamos a criação da engine e da SessionLocal
86
- # conforme o "banco atual" selecionado (ex.: prod/test/treinamento).
87
  try:
88
  from db_router import (
89
  get_engine as _router_get_engine,
90
  get_session_factory as _router_get_session_factory,
91
  SessionLocal as _router_SessionLocal,
 
 
92
  )
93
  _HAS_ROUTER = True
94
  except Exception:
95
  _HAS_ROUTER = False
 
 
 
 
 
96
 
97
 
98
  # =========================================================
@@ -139,22 +149,17 @@ def _build_fallback_uri() -> str:
139
  # 5) Engine / SessionLocal
140
  # =========================================================
141
  # Observação importante:
142
- # - Se houver db_router, usamos as fábricas do roteador (engine/sessões por ambiente).
143
  # - Caso contrário, criamos uma engine única a partir de DATABASE_URL/DB_* ou SQLite.
144
 
145
  if _HAS_ROUTER:
146
  # =========== Com roteador ===========
147
  def get_engine():
148
- """
149
- Retorna a engine do banco ATUAL (prod/test/treinamento),
150
- conforme implementado pelo seu db_router.get_engine().
151
- """
152
  return _router_get_engine()
153
 
154
- def _session_factory():
155
- """
156
- Fábrica de Session para o banco ATUAL (vinda do roteador).
157
- """
158
  return _router_get_session_factory()
159
 
160
  # SessionLocal fornecida pelo roteador (respeita o banco atual)
@@ -177,33 +182,47 @@ else:
177
 
178
  def get_engine():
179
  """
180
- Engine fixa do fallback. Se quiser trocar de banco em runtime sem roteador,
181
- você precisará recriar a engine manualmente (ou adotar db_router).
182
  """
183
  return _engine
184
 
185
- _SessionFactory = sessionmaker(
186
- autocommit=False,
187
- autoflush=False,
188
- bind=_engine,
189
- )
190
-
191
- def _session_factory():
192
- return _SessionFactory
193
 
194
  # Para compatibilidade com código que usa SessionLocal()
195
- SessionLocal = _SessionFactory
196
 
197
 
198
  # =========================================================
199
- # 6) Expor 'engine' e Base ORM
200
  # =========================================================
201
- # Atenção: 'engine' é resolvido no momento da importação.
202
- # Se você troca de banco após importar 'banco', prefira usar get_engine()
203
- # e criar a sessão com SessionLocal() para assegurar o banco ATUAL.
204
- engine = get_engine()
205
  Base = declarative_base()
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  # =========================================================
209
  # 7) Utilitários (opcionais)
@@ -211,13 +230,12 @@ Base = declarative_base()
211
  def init_schema():
212
  """
213
  Cria/atualiza as tabelas no banco ATUAL.
214
- • Com roteador: aplica no banco escolhido (Produção/Teste/...).
215
  • Sem roteador: aplica no DATABASE_URL padrão.
216
 
217
  Use em DEV/TESTE; em produção, prefira migrações (ex.: Alembic).
218
  """
219
  # Importa 'models' de forma tardia/segura para registrar mapeamentos
220
- import importlib
221
  try:
222
  importlib.import_module("models")
223
  except ModuleNotFoundError:
@@ -240,7 +258,15 @@ def db_info() -> dict:
240
  url = DATABASE_URL # type: ignore[name-defined]
241
  except Exception:
242
  url = "(não disponível)"
243
- return {
244
- "url": url,
245
- "using_router": _HAS_ROUTER,
246
- }
 
 
 
 
 
 
 
 
 
6
  - Fallback: um único DATABASE_URL vindo de env/Secrets
7
  - Postgres / MySQL / SQLite (c/ alias de case para load.db no Linux)
8
  - Garantia de criação do diretório pai do SQLite (evita 'unable to open database file')
9
+
10
+ Principais melhorias:
11
+ - 'engine' agora é um PROXY dinâmico (quando houver db_router), refletindo a escolha atual.
12
+ - Mantém API compatível: engine, Base, SessionLocal, db_info(), get_engine(), etc.
13
  """
14
 
15
  import os
16
  import shutil
17
+ import importlib
18
+ from typing import Optional
19
+
20
  from sqlalchemy import create_engine
21
  from sqlalchemy.orm import sessionmaker, declarative_base
22
  from dotenv import load_dotenv
 
66
  if not sqlite_url or not sqlite_url.startswith("sqlite"):
67
  return sqlite_url
68
 
 
69
  # Formatos: sqlite:///relativo.db | sqlite:////abs/path/to.db
70
  path = sqlite_url.replace("sqlite:///", "", 1)
 
71
  if path.startswith("//"):
72
  path = path[1:]
73
  file_path = os.path.abspath(path)
 
85
 
86
 
87
  # =========================================================
88
+ # 3) Suporte a roteador (db_router.py) — preferencial
89
  # =========================================================
 
 
90
  try:
91
  from db_router import (
92
  get_engine as _router_get_engine,
93
  get_session_factory as _router_get_session_factory,
94
  SessionLocal as _router_SessionLocal,
95
+ current_db_choice as _router_current_choice,
96
+ bank_label as _router_bank_label,
97
  )
98
  _HAS_ROUTER = True
99
  except Exception:
100
  _HAS_ROUTER = False
101
+ _router_get_engine = None
102
+ _router_get_session_factory = None
103
+ _router_SessionLocal = None
104
+ _router_current_choice = None
105
+ _router_bank_label = None
106
 
107
 
108
  # =========================================================
 
149
  # 5) Engine / SessionLocal
150
  # =========================================================
151
  # Observação importante:
152
+ # - Se houver db_router, usar SEMPRE as fábricas do roteador (engine/sessões por ambiente).
153
  # - Caso contrário, criamos uma engine única a partir de DATABASE_URL/DB_* ou SQLite.
154
 
155
  if _HAS_ROUTER:
156
  # =========== Com roteador ===========
157
  def get_engine():
158
+ """Retorna a engine do banco ATUAL (prod/test/treinamento)."""
 
 
 
159
  return _router_get_engine()
160
 
161
+ def get_session_factory():
162
+ """Retorna um sessionmaker para o banco ATUAL (do roteador)."""
 
 
163
  return _router_get_session_factory()
164
 
165
  # SessionLocal fornecida pelo roteador (respeita o banco atual)
 
182
 
183
  def get_engine():
184
  """
185
+ Engine fixa do fallback. Para trocar de banco em runtime sem roteador,
186
+ é necessário recriar a engine manualmente.
187
  """
188
  return _engine
189
 
190
+ def get_session_factory():
191
+ """Fábrica de sessão fixa (fallback)."""
192
+ return sessionmaker(autocommit=False, autoflush=False, bind=_engine)
 
 
 
 
 
193
 
194
  # Para compatibilidade com código que usa SessionLocal()
195
+ SessionLocal = get_session_factory()
196
 
197
 
198
  # =========================================================
199
+ # 6) Expor 'engine' DINÂMICA e Base ORM
200
  # =========================================================
 
 
 
 
201
  Base = declarative_base()
202
 
203
+ class _EngineProxy:
204
+ """
205
+ Proxy leve que encaminha atributos/operções para a engine ATUAL.
206
+ Isso permite manter 'engine' importável sem cristalizar a escolha de banco.
207
+ """
208
+ def __getattr__(self, name):
209
+ return getattr(get_engine(), name)
210
+
211
+ def __repr__(self):
212
+ eng = get_engine()
213
+ try:
214
+ url = str(eng.url)
215
+ except Exception:
216
+ url = "(url indisponível)"
217
+ if _HAS_ROUTER and _router_current_choice:
218
+ ch = _router_current_choice()
219
+ lbl = _router_bank_label(ch) if _router_bank_label else ch
220
+ return f"<EngineProxy choice={ch} label={lbl} url={url}>"
221
+ return f"<EngineProxy url={url}>"
222
+
223
+ # 'engine' exportado como proxy (dinâmico)
224
+ engine = _EngineProxy()
225
+
226
 
227
  # =========================================================
228
  # 7) Utilitários (opcionais)
 
230
  def init_schema():
231
  """
232
  Cria/atualiza as tabelas no banco ATUAL.
233
+ • Com roteador: aplica no banco escolhido (Produção/Teste/Treinamento).
234
  • Sem roteador: aplica no DATABASE_URL padrão.
235
 
236
  Use em DEV/TESTE; em produção, prefira migrações (ex.: Alembic).
237
  """
238
  # Importa 'models' de forma tardia/segura para registrar mapeamentos
 
239
  try:
240
  importlib.import_module("models")
241
  except ModuleNotFoundError:
 
258
  url = DATABASE_URL # type: ignore[name-defined]
259
  except Exception:
260
  url = "(não disponível)"
261
+
262
+ info = {"url": url, "using_router": _HAS_ROUTER}
263
+
264
+ if _HAS_ROUTER and _router_current_choice:
265
+ ch = _router_current_choice()
266
+ info["choice"] = ch
267
+ try:
268
+ info["label"] = _router_bank_label(ch)
269
+ except Exception:
270
+ info["label"] = ch
271
+
272
+ return info