Files
lifetrack/apps/api/app/core/database.py
T
MeeJayandClaude Opus 5 93f0689c1e Initial import: LifeTrack v1 (santé, vape, finances)
Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de
pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et
finances personnelles avec import de relevés bancaires.

Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS +
Vite + Tailwind + ECharts, déploiement Docker Compose. Modules
auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework
de connecteurs à deux voies (importeurs de fichiers + ingestion JSON)
pour brancher de nouvelles sources sans toucher au noyau.

Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié
contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16
(28 tables, SPA servie par nginx, wizard de premier démarrage).

Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les
documents de conception et de recherche dans docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:48:57 +02:00

46 lines
1.5 KiB
Python

from collections.abc import Iterator
from typing import Any
from sqlalchemy import Engine, create_engine, event
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
from sqlalchemy.pool import StaticPool
from app.core.config import get_settings
class Base(DeclarativeBase):
"""Single declarative base for the whole application."""
def _build_engine(url: str) -> Engine:
if url.startswith("sqlite"):
# Test configuration (pytest runs on SQLite in-memory): a StaticPool
# shares the single in-memory database across sessions/threads.
return create_engine(
url,
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
return create_engine(url, pool_pre_ping=True)
engine = _build_engine(get_settings().database_url)
if engine.dialect.name == "sqlite":
# SQLite does not enforce foreign keys (and thus ON DELETE CASCADE, used by
# the import rollback) unless the pragma is enabled per connection.
@event.listens_for(engine, "connect")
def _enable_sqlite_fks(dbapi_connection: Any, _record: Any) -> None:
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)
def get_db() -> Iterator[Session]:
"""FastAPI dependency: one session per request, closed automatically."""
with SessionLocal() as session:
yield session