--- # nuzlocke-tracker-g8zi title: 'Fix Pokemon form identification: separate PokeAPI ID from national dex' status: completed type: bug priority: high created_at: 2026-02-07T13:44:25Z updated_at: 2026-02-07T13:54:29Z blocking: - 6aje --- ## Problem Pokemon forms (Alolan, Galarian, etc.) share a national dex number with their base species (e.g., Alolan Rattata is still national dex #19, same as regular Rattata). However, the current data model uses the PokeAPI internal ID (e.g., 10091 for Alolan Rattata) as the `national_dex` field. This is semantically wrong and causes: 1. **Wrong display**: Frontend shows "#10091" next to Alolan Rattata instead of "#19" 2. **Broken sorting**: Forms sort at the end of the Pokemon list instead of next to their base species 3. **Misleading data**: The field name implies a national Pokedex number but contains an internal API ID ## Current architecture The `national_dex` field is deeply embedded as the unique Pokemon identifier: - **Database**: `SmallInteger` column with `UNIQUE` constraint (`alembic/versions/03e5f186a9d5`) - **Models**: `pokemon.py` — `mapped_column(SmallInteger, unique=True)` - **Seeder**: `loader.py` — upsert conflict resolution on `national_dex`, builds `{national_dex: id}` mapping for linking encounters and evolutions - **Seed data**: `fetch_pokeapi.py` — uses PokeAPI pokemon ID as `national_dex` for both base species and forms - **API**: All CRUD operations key on `national_dex`, returned as `nationalDex` in JSON - **Frontend**: Displayed as `#nationalDex` in Pokemon selector, admin table, encounter modals ## Proposed fix Add a `pokemon_id` (or similar) field as the true unique identifier (the PokeAPI pokemon ID), and keep `national_dex` as the real national dex number (shared between forms). This requires changes across every layer. ## Checklist - [ ] Add new migration: add `pokemon_id` column (unique, not null), change `national_dex` unique constraint to non-unique - [ ] Update Pokemon model to add `pokemon_id` field - [ ] Update seed data: `pokemon.json` entries get both `pokemon_id` (PokeAPI ID) and `national_dex` (real dex number, from species endpoint) - [ ] Update `fetch_pokeapi.py`: for forms, look up the species to get the real national dex, store PokeAPI ID separately - [ ] Update `loader.py`: upsert on `pokemon_id` instead of `national_dex`, update encounter/evolution linking - [ ] Update API schemas and endpoints to expose both fields - [ ] Update frontend to display real `national_dex` but use `pokemon_id` internally for uniqueness - [ ] Update encounter seed data to reference `pokemon_id` instead of `national_dex` ## Impact Touches almost every layer: migration, model, seeder, API, frontend. Should be done before more forms are added (bean 6aje) to avoid migrating bad data.