Phase 3: Schema Layer - Pydantic v2 migration, deduplication, and missing schemas

- Migrated all schemas to Pydantic v2 syntax (model_config, ConfigDict)
- Fixed mutable default in ProtoMessageBase using Field(default_factory=datetime.now)
- Consolidated CardCollection and Wishlist schemas in user_card_collection.py
- Created game_schemas.py with GameCreate, GameResponse, GameJoinRequest, etc.
- Created mtg_card_schemas.py with MtgCardResponse, MtgCardSearchRequest, etc.
- Added CardImportBatchCreate, CardImportBatchResponse, UserCardImportCreate/Response schemas
- Fixed duplicate UserCardImportRecord class between card_import_batch.py and user_card_import_record.py
- Updated __init__.py with comprehensive schema exports
- Created verify_schemas.py for schema-model matching verification
This commit is contained in:
2026-08-16 05:22:17 +00:00
parent 5f324cf8a9
commit bea91db64d
20 changed files with 2733 additions and 342 deletions
+122
View File
@@ -0,0 +1,122 @@
"""Pydantic schemas for MTG card data."""
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional, List, Dict, Any
from datetime import datetime
class MtgCardResponse(BaseModel):
"""MTG card response with full details."""
id: int
source_id: Optional[int] = None
name: str
mana_cost: Optional[str] = None
type_line: Optional[str] = None
oracle_text: Optional[str] = None
power: Optional[str] = None
toughness: Optional[str] = None
rarity: Optional[str] = None
layout: Optional[str] = None
artist: Optional[str] = None
flavor_text: Optional[str] = None
numbers: Optional[str] = None
identifiers: Optional[Dict[str, Any]] = None
images: Optional[Dict[str, Any]] = None
image: Optional[str] = None
card_parts: Optional[List[str]] = None
keywords: Optional[List[str]] = None
legalities: Optional[Dict[str, str]] = None
set_code: Optional[str] = None
set_name: Optional[str] = None
synced_at: Optional[datetime] = None
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class MtgCardSearchRequest(BaseModel):
"""MTG card search request."""
query: str = Field(..., min_length=1, max_length=100)
set_code: Optional[str] = None
rarity: Optional[str] = None
type_line: Optional[str] = None
limit: int = Field(50, ge=1, le=200)
offset: int = Field(0, ge=0)
class MtgCardSearchResponse(BaseModel):
"""MTG card search response."""
cards: List[MtgCardResponse]
total: int
page: int
page_size: int
total_pages: int
class MtgSetResponse(BaseModel):
"""MTG set response."""
id: int
code: str
name: str
release_date: Optional[datetime] = None
card_count: Optional[int] = None
type: Optional[str] = None
border: Optional[str] = None
mcm_id: Optional[int] = None
model_config = ConfigDict(from_attributes=True)
class MtgCardMirrorResponse(BaseModel):
"""Mirrored card data for user decks."""
id: int
source_id: Optional[int] = None
name: str
mana_cost: Optional[str] = None
type_line: Optional[str] = None
oracle_text: Optional[str] = None
power: Optional[str] = None
toughness: Optional[str] = None
rarity: Optional[str] = None
layout: Optional[str] = None
artist: Optional[str] = None
flavor_text: Optional[str] = None
numbers: Optional[str] = None
identifiers: Optional[Dict[str, Any]] = None
images: Optional[Dict[str, Any]] = None
image: Optional[str] = None
card_parts: Optional[str] = None
keywords: Optional[str] = None
legalities: Optional[Dict[str, str]] = None
set_code: Optional[str] = None
set_name: Optional[str] = None
synced_at: Optional[datetime] = None
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class DeckCardLinkResponse(BaseModel):
"""Deck-card link response."""
id: int
deck_id: int
card_id: int
quantity: int
zone: str
card: MtgCardMirrorResponse
model_config = ConfigDict(from_attributes=True)
class DeckWithCardsResponse(BaseModel):
"""Deck response with card links."""
id: int
name: str
content: str
format: str
status: str
folder_id: Optional[int] = None
owner_id: int
creation_date: datetime
card_links: List[DeckCardLinkResponse] = []
model_config = ConfigDict(from_attributes=True)