Add nullable region field to evolutions for regional form filtering

Regional evolutions (e.g., Pikachu → Alolan Raichu) only occur in specific
regions. This adds a nullable region column so the app can filter evolutions
by the game's region. When a regional evolution exists for a given trigger/item,
the non-regional counterpart is automatically hidden.

Full-stack: migration, model, schemas, API with region query param, seeder,
Go fetch tool, frontend types/API/hook/components, and admin form.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 20:05:07 +01:00
parent 23a7b6ad53
commit a65efa22da
17 changed files with 147 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import func, select
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload, selectinload
@@ -121,18 +121,40 @@ async def get_pokemon(
@router.get("/pokemon/{pokemon_id}/evolutions", response_model=list[EvolutionResponse])
async def get_pokemon_evolutions(
pokemon_id: int, session: AsyncSession = Depends(get_session)
pokemon_id: int,
region: str | None = Query(None),
session: AsyncSession = Depends(get_session),
):
pokemon = await session.get(Pokemon, pokemon_id)
if pokemon is None:
raise HTTPException(status_code=404, detail="Pokemon not found")
result = await session.execute(
query = (
select(Evolution)
.where(Evolution.from_pokemon_id == pokemon_id)
.options(joinedload(Evolution.to_pokemon))
)
return result.scalars().unique().all()
if region is not None:
query = query.where(
or_(Evolution.region.is_(None), Evolution.region == region)
)
result = await session.execute(query)
evolutions = result.scalars().unique().all()
if region is not None:
# Regional evolutions replace the non-regional one that shares the
# same trigger + item (e.g. Pikachu + thunder-stone → Alolan Raichu
# replaces Pikachu + thunder-stone → Raichu in Alola).
regional_keys = {
(e.trigger, e.item) for e in evolutions if e.region is not None
}
if regional_keys:
evolutions = [
e for e in evolutions
if e.region is not None or (e.trigger, e.item) not in regional_keys
]
return evolutions
@router.put("/pokemon/{pokemon_id}", response_model=PokemonResponse)