import os from pathlib import Path from sqlalchemy import create_engine, event from sqlalchemy.orm import DeclarativeBase, sessionmaker # The one database. It is SQLite, on this machine, in a file. # # Upstream could also point at a server database — `AIDND_DATABASE_URL` or the # platform-conventional `DATABASE_URL`, normalised onto psycopg3, with # pre-ping for a serverless Postgres that suspends when idle. That existed for # a hosted deployment. M2 removed it along with the deployment: a local # single-user storyteller has one reader, and a network database would be one # more thing that has to be running, and one more place the story lives. # # `AIDND_DB_PATH` stays. It is how the Docker image puts the database on a # volume, and how a test points at a throwaway file. _env_db_path = os.environ.get("AIDND_DB_PATH") DB_PATH = ( Path(_env_db_path).resolve() if _env_db_path else Path(__file__).resolve().parent.parent / "data.db" ) DB_PATH.parent.mkdir(parents=True, exist_ok=True) engine = create_engine( f"sqlite:///{DB_PATH}", connect_args={"check_same_thread": False}, ) @event.listens_for(engine, "connect") def _enable_sqlite_foreign_keys(dbapi_connection, _record): # SQLite ships with foreign keys OFF per connection; without this every # ondelete=CASCADE/SET NULL in models.py is silently ignored. cursor = dbapi_connection.cursor() cursor.execute("PRAGMA foreign_keys=ON") cursor.close() SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False) class Base(DeclarativeBase): pass def get_db(): db = SessionLocal() try: yield db finally: db.close()