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
This commit is contained in:
+152
-62
@@ -1,22 +1,75 @@
|
||||
"""
|
||||
Database engine and session management.
|
||||
|
||||
Provides async SQLAlchemy engine and session factory for dependency injection.
|
||||
Supports dual database connections for mtgonline app and mtgjson data.
|
||||
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.
|
||||
"""
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
|
||||
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.settings import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
from app.core.config import get_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
config = get_config()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Engine setup — single engine, auto-detect SQLite vs PostgreSQL
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Primary database engine (mtgonline app)
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=settings.DEBUG,
|
||||
pool_pre_ping=True,
|
||||
pool_size=20,
|
||||
max_overflow=10,
|
||||
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(
|
||||
@@ -25,28 +78,43 @@ async_session = async_sessionmaker(
|
||||
expire_on_commit=False,
|
||||
)
|
||||
|
||||
# Secondary database engine (mtgjson data)
|
||||
mtg_engine = create_async_engine(
|
||||
settings.MTG_DATABASE_URL,
|
||||
echo=settings.DEBUG,
|
||||
pool_pre_ping=True,
|
||||
pool_size=10,
|
||||
max_overflow=5,
|
||||
)
|
||||
|
||||
mtg_async_session = async_sessionmaker(
|
||||
mtg_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
|
||||
# Mirror engine (same as primary — mirrors live in mtgo_platform)
|
||||
mirror_engine = engine
|
||||
mirror_async_session = async_session
|
||||
# ---------------------------------------------------------------------------
|
||||
# Base class for ORM models
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
"""FastAPI dependency that provides a database session for the mtgonline app."""
|
||||
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
|
||||
@@ -58,46 +126,68 @@ async def get_db() -> AsyncSession:
|
||||
await session.close()
|
||||
|
||||
|
||||
async def mtg_get_db() -> AsyncSession:
|
||||
"""FastAPI dependency that provides a database session for mtgjson data."""
|
||||
async with mtg_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 mirror_get_db() -> AsyncSession:
|
||||
"""FastAPI dependency that provides a database session for mirror operations."""
|
||||
async with mirror_async_session() as session:
|
||||
try:
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
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'})"
|
||||
)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""Base class for all ORM models."""
|
||||
pass
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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",
|
||||
"get_db",
|
||||
"async_session",
|
||||
"engine",
|
||||
"mtg_get_db",
|
||||
"mtg_async_session",
|
||||
"async_session",
|
||||
"get_db",
|
||||
"create_tables",
|
||||
# Backward-compatible aliases
|
||||
"mtg_engine",
|
||||
"mtg_async_session",
|
||||
"mtg_get_db",
|
||||
"mirror_get_db",
|
||||
"mirror_async_session",
|
||||
"mirror_engine",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user