fix: resolve secondary FKs in nested POST (event_badge_template_id)
In the nested POST handler (api_crud_v3_nested.py), sanitize_payload was running before model instantiation. For secondary FK fields like event_badge_template_id, sanitize_payload resolved the random string → integer, then the model's root_validator stripped the integer back to None (Vision ID anti-leakage guard). Only the parent FK survived because it was explicitly re-injected after serialization. Fix: moved sanitize_payload to run on data_to_insert after serialization, matching the flat V3 POST pattern (api_crud_v3.py). Also moved account_id injection to after sanitize_payload, fixing a latent bug where account_id was silently written as NULL on non-bypass auth. Adds regression test to test_e2e_v3_demo_parity.py that creates an event_badge via nested POST with event_badge_template_id and verifies the field is non-None in the response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -313,15 +313,6 @@ async def post_child_obj(
|
||||
if not table_name_insert or not input_model or not table_name_select or not output_model:
|
||||
return mk_resp(data=False, status_code=500, response=response, status_message=f"Configuration error.")
|
||||
|
||||
if not account.super and account.auth_method != 'bypass' and account.account_id:
|
||||
if 'account_id' in input_model.__fields__:
|
||||
obj_data['account_id'] = account.account_id
|
||||
|
||||
obj_data[f'{parent_obj_type}_id'] = resolved_parent_id
|
||||
|
||||
# Sanitize payload (ID resolution, virtual fields, and optionally extra fields)
|
||||
sanitize_payload(obj_data, input_model, ignore_extra=x_ae_ignore_extra_fields)
|
||||
|
||||
try:
|
||||
validated_obj = input_model(**obj_data)
|
||||
except ValidationError as e:
|
||||
@@ -332,8 +323,21 @@ async def post_child_obj(
|
||||
|
||||
data_to_insert = validated_obj.dict(exclude_unset=True)
|
||||
|
||||
# Re-inject parent FK after model serialization. Some model root_validators strip
|
||||
# integer IDs (a Vision ID anti-leakage guard) which would drop the FK from the dict.
|
||||
# Sanitize AFTER serialization so that:
|
||||
# 1. The model receives raw Vision ID strings (passes field-length constraints).
|
||||
# 2. ID resolution (string → integer) happens on the dict going to the DB,
|
||||
# avoiding the root_validator's integer-stripping anti-leakage guard.
|
||||
# (Matches the flat V3 POST pattern in api_crud_v3.py.)
|
||||
sanitize_payload(data_to_insert, input_model, ignore_extra=x_ae_ignore_extra_fields)
|
||||
|
||||
# Enforce account ownership AFTER sanitize_payload so the integer account_id goes
|
||||
# straight to the DB without conflicting with Vision ID string constraints in the model.
|
||||
if not account.super and account.auth_method != 'bypass' and account.account_id:
|
||||
if 'account_id' in input_model.__fields__:
|
||||
data_to_insert['account_id'] = account.account_id
|
||||
|
||||
# Re-inject parent FK last — overrides anything sanitize_payload or the model may have
|
||||
# set — ensuring the child is always linked to the correct parent.
|
||||
data_to_insert[f'{parent_obj_type}_id'] = resolved_parent_id
|
||||
|
||||
if sql_insert_result := sql_insert(data=data_to_insert, table_name=table_name_insert):
|
||||
|
||||
Reference in New Issue
Block a user