docs(data_store): document polymorphic for_id resolution pattern

Clarifies why v_data_store has no for_id_random JOIN column (for_type
is runtime-variable), how get_id_random() resolves the integer FK at
model validation time, and that Step 4 guarantees integers never leak
into API responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-17 11:25:08 -04:00
parent 30d2580639
commit 0bb65efb7c
2 changed files with 12 additions and 0 deletions

View File

@@ -106,6 +106,10 @@ class Data_Store_Base(BaseModel):
values[f'{field}_random'] = resolved_rid values[f'{field}_random'] = resolved_rid
# 3. Handle Polymorphic for_id # 3. Handle Polymorphic for_id
# v_data_store intentionally has no for_id_random JOIN column because for_type is
# runtime-variable (event, event_session, person, journal, etc.) — the target table
# is unknown at view-definition time. Resolution falls back to get_id_random() here.
# If resolution fails, Step 4 strips the raw integer to None — integers never leak.
if f_rid := values.get('for_id_random'): if f_rid := values.get('for_id_random'):
values['for_id'] = f_rid values['for_id'] = f_rid
elif values.get('for_id') and values.get('for_type'): elif values.get('for_id') and values.get('for_type'):

View File

@@ -261,6 +261,14 @@ Fix: add a mapping to the special-case block in `sanitize_payload()` (same file,
if obj_type_lookup == 'poc_person': obj_type_lookup = 'person' if obj_type_lookup == 'poc_person': obj_type_lookup = 'person'
``` ```
#### Polymorphic FK fields (`for_id` / `for_type`)
`data_store.for_id` is a polymorphic FK — `for_type` can be `event`, `event_session`, `person`, `journal`, or any other Aether object type, chosen at runtime. Because the target table is unknown at view-definition time, `v_data_store` intentionally **has no `for_id_random` JOIN column** (unlike `account_id_random`, which is a fixed JOIN to `account`).
Resolution happens in the `Data_Store_Base.map_v3_ids` root validator (Step 3): it calls `get_id_random(for_id_int, for_type)` which does `SELECT id_random FROM {for_type} WHERE id = :id`, with Redis caching. If resolution fails, Step 4 strips the raw integer to `None`**integers never appear in API responses**.
> If you see `for_id: null` in a response where a value is expected, check that `for_type` matches a valid Aether table name and the referenced record exists.
#### Alt-view fields (in `tbl_alt` only) #### Alt-view fields (in `tbl_alt` only)
Some objects have a richer alternate SQL view triggered by `?view=alt`. These fields Some objects have a richer alternate SQL view triggered by `?view=alt`. These fields
**must still be declared in the Pydantic model and `searchable_fields`** even if they only **must still be declared in the Pydantic model and `searchable_fields`** even if they only