This commit is contained in:
timka123 committed 2026-10-18 18:02:05 +03:00
commit 17a0a3c4c9
121 files changed
+17641

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
"""Database engine and session plumbing.
Synchronous SQLAlchemy on purpose: the background scheduler, the Telegram
polling bot and the request handlers all touch the same tables, and one boring
threadsafe sessionmaker is far easier to reason about than three event loops.
FastAPI runs plain `def` endpoints in a threadpool, so nothing blocks.
"""
from __future__ import annotations
from collections.abc import Iterator
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
from app.config import env_settings
class Base(DeclarativeBase):
pass
def _engine_kwargs(url: str) -> dict:
if url.startswith("sqlite"):
# One file, many threads (scheduler + bot + requests).
return {"connect_args": {"check_same_thread": False}}
return {"pool_pre_ping": True, "pool_size": 5, "max_overflow": 10}
engine = create_engine(env_settings().database_url, **_engine_kwargs(env_settings().database_url))
SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)
def init_db() -> None:
"""Create any missing tables. The schema is additive, so this is enough."""
from app import models # noqa: F401 (register the mappers)
Base.metadata.create_all(bind=engine)
@contextmanager
def session_scope() -> Iterator[Session]:
"""A transactional session for background jobs and scripts."""
session = SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
def get_session() -> Iterator[Session]:
"""FastAPI dependency: one session per request, committed by the endpoint."""
session = SessionLocal()
try:
yield session
finally:
session.close()