refactor: consolidate 10+ model files into 2 modules
- models.py: All application models (users, decks, sessions, groups, imports, etc.) - mtg_models.py: All MTG card data models (sets, cards, mirrors, links) - Removed: mirror_models.py, user_data.py, user_deck.py, card_import_batch.py, user_card_import.py, user_card_import_record.py, relationships.py - Added __backup__/ directory with original files for reference - DecklistFile.card_links cross-model relationship resolved via late import
This commit is contained in:
@@ -1,14 +1,29 @@
|
||||
"""
|
||||
SQLAlchemy ORM models for the MTG database (mtgjson.com data).
|
||||
SQLAlchemy ORM models for MTG card data and mirrors.
|
||||
|
||||
Models mirror the mtg_cards and mtg_sets tables in the MTG PostgreSQL database.
|
||||
Consolidated from mtg_models.py and mirror_models.py into a single module.
|
||||
|
||||
This file contains all MTG card data models:
|
||||
- MtgSet, MtgCard: Canonical card/set data from mtgjson.com (mtg_data DB)
|
||||
- MtgCardMirror: Mirrored card data copied to mtgonline DB for fast deckbuilding queries
|
||||
- DeckCardLink: Junction table linking user decks to mirrored cards
|
||||
|
||||
Cross-model references to application models (e.g., DeckCardLink.deck → DecklistFile)
|
||||
use string-based relationship() calls to avoid circular imports.
|
||||
"""
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Index
|
||||
from sqlalchemy import (
|
||||
Column, Integer, String, Text, DateTime, ForeignKey, Index,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MTG Set and Card (canonical data from mtgjson.com)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class MtgSet(Base):
|
||||
"""MTG Set model."""
|
||||
__tablename__ = "mtg_sets"
|
||||
@@ -65,7 +80,105 @@ class MtgCard(Base):
|
||||
return f"<MtgCard {self.name} ({self.set_id})>"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Card Mirror and Deck Links (mtgonline DB)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class MtgCardMirror(Base):
|
||||
"""
|
||||
Mirrored card data for user decks.
|
||||
|
||||
Contains all relevant card fields copied from mtg_cards (mtg_data)
|
||||
to avoid cross-DB joins during deckbuilding. Back-references source_id
|
||||
to the canonical mtg_cards.id for sync tracking.
|
||||
"""
|
||||
__tablename__ = "mtg_cards_mirror"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
source_id = Column(
|
||||
Integer,
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="Logical reference to mtg_cards.id in mtg_data database "
|
||||
"(cross-DB reference, no FK constraint)",
|
||||
)
|
||||
name = Column(String(255), nullable=False, index=True)
|
||||
mana_cost = Column(String(255), nullable=True)
|
||||
type_line = Column(String(255), nullable=True)
|
||||
oracle_text = Column(Text, nullable=True)
|
||||
power = Column(String(50), nullable=True)
|
||||
toughness = Column(String(50), nullable=True)
|
||||
rarity = Column(String(50), nullable=True)
|
||||
layout = Column(String(50), nullable=True)
|
||||
artist = Column(String(255), nullable=True)
|
||||
flavor_text = Column(Text, nullable=True)
|
||||
numbers = Column(String(100), nullable=True)
|
||||
identifiers = Column(Text, nullable=True) # JSON string of all identifiers
|
||||
images = Column(Text, nullable=True) # JSON string of image URLs
|
||||
image = Column(Text, nullable=True) # Card image URL from MTGJSON
|
||||
card_parts = Column(Text, nullable=True) # Comma-separated list of face names
|
||||
keywords = Column(Text, nullable=True) # Comma-separated keywords
|
||||
legalities = Column(Text, nullable=True) # JSON of format legality
|
||||
set_code = Column(String(10), nullable=True, index=True)
|
||||
set_name = Column(String(255), nullable=True)
|
||||
# Sync tracking
|
||||
synced_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
|
||||
# Relationships
|
||||
deck_links = relationship(
|
||||
"DeckCardLink",
|
||||
back_populates="card",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<MtgCardMirror {self.name} (ID: {self.id}, source: {self.source_id})>"
|
||||
|
||||
|
||||
class DeckCardLink(Base):
|
||||
"""
|
||||
Junction table linking user decks to mirrored cards.
|
||||
|
||||
Represents a specific quantity of a mirrored card in a specific zone
|
||||
(main deck or sideboard) of a user's deck.
|
||||
"""
|
||||
__tablename__ = "deck_card_links"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
deck_id = Column(
|
||||
Integer, ForeignKey("mtgonline_decklist_files.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
card_id = Column(
|
||||
Integer, ForeignKey("mtg_cards_mirror.id"), nullable=False,
|
||||
)
|
||||
quantity = Column(Integer, nullable=False, default=1)
|
||||
zone = Column(String(20), nullable=False, default="main") # 'main' or 'sideboard'
|
||||
|
||||
# Composite unique: a card can only appear once per zone in a deck
|
||||
__table_args__ = (
|
||||
UniqueConstraint("deck_id", "card_id", "zone", name="uq_deck_card_link"),
|
||||
Index("idx_deck_card_deck", "deck_id"),
|
||||
Index("idx_deck_card_card", "card_id"),
|
||||
)
|
||||
|
||||
# Relationships
|
||||
# String reference to DecklistFile in models.py to avoid circular imports
|
||||
deck = relationship("DecklistFile", back_populates="card_links")
|
||||
card = relationship("MtgCardMirror", back_populates="deck_links")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<DeckCardLink deck={self.deck_id} card={self.card_id} "
|
||||
f"qty={self.quantity} zone={self.zone}>"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Indexes for performance
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Index("idx_mtg_cards_name_set", MtgCard.name, MtgCard.set_id)
|
||||
Index("idx_mtg_cards_type", MtgCard.type_line)
|
||||
Index("idx_mtg_cards_rarity", MtgCard.rarity)
|
||||
|
||||
Reference in New Issue
Block a user