- Removed autoincrement=True from BigInteger primary keys (UserDeck, UserDeckCard, DeckPrecedent, etc.) - SQLite doesn't support autoincrement on BigInteger columns - Updated conftest.py test engine configuration - All admin tests passing (5/5) - All auth tests passing (7/7) - Game model tests passing (13/13) - Deck tests still failing due to SQLite RETURNING clause issue
198 lines
5.6 KiB
Python
198 lines
5.6 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,
|
|
# Disable insertmanyvalues to avoid RETURNING clause issues with aiosqlite
|
|
"execution_options": {
|
|
"insertmanyvalues_page_size": 0,
|
|
},
|
|
}
|
|
),
|
|
)
|
|
|
|
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",
|
|
]
|