Files
mtgonline/backend/app/core/database.py
T
akadmin 900f51af3a Complete architecture simplification: 54+ files → 37 files
- Removed 11 duplicate router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py)
- Removed duplicate core files (logging_config.py, settings.py)
- Removed unused services (card_mirror_service.py, mtgjson_loader.py, mtgjson_uploader.py)
- Removed unused schemas (proto_messages.py, protocol_constants.py)
- Created run.py startup script for simple execution
- Created app/core/config.py for unified configuration management
- Created app/core/logging.py for logging configuration
- Consolidated routers into 6 focused files (auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py)
- Updated all imports to use consolidated model paths
- Added backward compatibility aliases in database.py
- Created SIMPLIFICATION_SUMMARY.md documentation

Architecture improvements:
- Single unified database engine with auto-detection (SQLite/PostgreSQL)
- Consolidated models into 2 files (models.py, mtg_models.py)
- Clean router structure with one file per domain
- Simplified configuration with sensible defaults
- SQLite by default for development, PostgreSQL for production

Testing:
- All imports verified working
- App loads successfully
- Ready for development and testing
2026-08-26 15:15:36 +00:00

194 lines
5.4 KiB
Python

"""
Database engine and session management.
Provides a single SQLAlchemy async engine supporting both SQLite (development)
and PostgreSQL (production) via a single DATABASE_URL configuration value.
## Usage
from app.core.database import get_db, create_tables, Base
# In FastAPI dependency:
async def endpoint(db: AsyncSession = Depends(get_db)):
...
# Create all tables:
await create_tables()
## Architecture
- Single engine for all database operations (no dual databases)
- Sync DeclarativeBase for model definitions
- Async engine and sessions for async operations
- Automatic dialect detection (SQLite vs PostgreSQL)
## Backward Compatibility
The following exports are provided for backward compatibility with code that
still references the old dual-database naming convention:
- ``mtg_engine`` → alias for ``engine``
- ``mtg_async_session`` → alias for ``async_session``
- ``mtg_get_db`` → alias for ``get_db``
- ``mirror_get_db`` → alias for ``get_db``
These should be removed as callers are migrated to the new unified API.
"""
import logging
from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import (
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase
from app.core.config import get_config
logger = logging.getLogger(__name__)
config = get_config()
# ---------------------------------------------------------------------------
# Engine setup — single engine, auto-detect SQLite vs PostgreSQL
# ---------------------------------------------------------------------------
engine = create_async_engine(
config.DATABASE_URL,
echo=config.DEBUG,
# SQLite doesn't support pool_size / max_overflow — only apply for PG
**(
{
"pool_pre_ping": True,
"pool_size": 20,
"max_overflow": 10,
}
if not config.is_sqlite
else {
"pool_pre_ping": False,
}
),
)
async_session = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
# ---------------------------------------------------------------------------
# Base class for ORM models
# ---------------------------------------------------------------------------
class Base(DeclarativeBase):
"""Base class for all ORM models.
Uses sync DeclarativeBase (not async-specific) for simplicity.
All models inherit from this class.
"""
pass
# ---------------------------------------------------------------------------
# FastAPI dependency — provides a database session
# ---------------------------------------------------------------------------
async def get_db() -> AsyncGenerator[AsyncSession, None]:
"""FastAPI dependency that provides a database session.
Yields an async session and handles commit/rollback/close automatically.
Usage in FastAPI endpoints:
from fastapi import Depends
from app.core.database import get_db
@app.get("/items")
async def list_items(db: AsyncSession = Depends(get_db)):
...
Yields:
An async SQLAlchemy session.
"""
async with async_session() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
# ---------------------------------------------------------------------------
# Table creation helper
# ---------------------------------------------------------------------------
async def create_tables() -> None:
"""Create all database tables from model metadata.
Imports all model modules to ensure Base.metadata is populated,
then creates all tables. This is intended for development and testing.
In production, use Alembic migrations instead.
Example:
>>> await create_tables()
"""
# Import all models to register them with Base.metadata
import app.models # noqa: F401
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
logger.info(
f"Database tables created successfully "
f"(dialect: {'SQLite' if config.is_sqlite else 'PostgreSQL'})"
)
# ---------------------------------------------------------------------------
# Backward-compatible aliases
#
# The old codebase referenced dual databases (mtg_engine, mtg_async_session,
# mtg_get_db, mirror_get_db). These aliases map them to the single unified
# engine so existing imports continue to work. They will be removed once all
# callers have been migrated.
# ---------------------------------------------------------------------------
# Alias: mtg_engine → engine (was the "primary" database engine)
mtg_engine = engine
# Alias: mtg_async_session → async_session (was the "primary" session factory)
mtg_async_session = async_session
# Alias: mtg_get_db → get_db (was the "primary" session dependency)
mtg_get_db = get_db
# Alias: mirror_get_db → get_db (was the "mirror" database session dependency)
mirror_get_db = get_db
# ---------------------------------------------------------------------------
# Exports
# ---------------------------------------------------------------------------
__all__ = [
"Base",
"engine",
"async_session",
"get_db",
"create_tables",
# Backward-compatible aliases
"mtg_engine",
"mtg_async_session",
"mtg_get_db",
"mirror_get_db",
]