feat(bot): модель данных, миграции Alembic, FSM в БД, TTL-чистка
- app/db/models.py: 13 таблиц (ARCHITECTURE §2.10); перечисления — VARCHAR + CHECK, JSON → JSONB на Postgres, BIGINT/INTEGER-ключи для SQLite - миграция 0001 (автогенерация + ручная чистка: импорт sa.Text, дубли CHECK) - миграции применяются при старте бота программно (app.db.migrate), script_location из пакета - SqlStorage: FSM aiogram поверх fsm_state — состояние переживает рестарт (перенесено из #4) - cleanup_loop: истёкшие черновики, снимки контейнеров и попытки входа старше суток - тесты: upgrade/downgrade, дрейф моделей и миграций, CHECK, CASCADE, FSM, чистка Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
"""Async engine and session factory."""
|
||||
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
SessionMaker = async_sessionmaker[AsyncSession]
|
||||
|
||||
|
||||
def make_engine(url: str) -> AsyncEngine:
|
||||
if url.startswith("sqlite"):
|
||||
# In-memory SQLite lives per connection: share one connection so every session sees the same DB.
|
||||
kwargs = (
|
||||
{"poolclass": StaticPool, "connect_args": {"check_same_thread": False}}
|
||||
if ":memory:" in url or url.endswith("://")
|
||||
else {}
|
||||
)
|
||||
engine = create_async_engine(url, **kwargs)
|
||||
event.listen(engine.sync_engine, "connect", _sqlite_foreign_keys)
|
||||
return engine
|
||||
return create_async_engine(url, pool_pre_ping=True)
|
||||
|
||||
|
||||
def _sqlite_foreign_keys(dbapi_conn: object, _record: object) -> None:
|
||||
# SQLite ignores FOREIGN KEY / ON DELETE CASCADE unless asked per connection
|
||||
cursor = dbapi_conn.cursor() # type: ignore[attr-defined]
|
||||
cursor.execute("PRAGMA foreign_keys=ON")
|
||||
cursor.close()
|
||||
|
||||
|
||||
def make_sessionmaker(engine: AsyncEngine) -> SessionMaker:
|
||||
return async_sessionmaker(engine, expire_on_commit=False)
|
||||
|
||||
|
||||
__all__ = ["SessionMaker", "make_engine", "make_sessionmaker"]
|
||||
Reference in New Issue
Block a user