Problem
services/player_service.py::update_by_squad_number_async copies the incoming
Pydantic model onto the ORM object one attribute at a time:
player.first_name = player_model.first_name
player.middle_name = player_model.middle_name
player.last_name = player_model.last_name
# ...ten assignments in total
Every field addition needs another line here, and a missed line fails silently
(the field just never updates). This is manual property mapping, a .NET/Java
habit. Note that create_async in the same module already uses the idiomatic
form — Player(**player_model.model_dump()) — so the two paths are
inconsistent.
Proposed Solution
Dump the validated model to a dict and assign with setattr in a loop.
model_dump() uses the Python field names (snake_case), which match the ORM
attribute names, so the mapping is 1:1.
for field, value in player_model.model_dump().items():
setattr(player, field, value)
The route layer already guarantees player_model.squad_number == squad_number
(400 on mismatch), so including the natural key in the loop is safe.
Suggested Approach
- Replace the ten assignments with the loop above.
- Decide whether to keep an explicit
player.squad_number = squad_number
after the loop (documents that the path parameter is authoritative) or rely
on the route guard — leave a one-line comment either way.
- Run
uv run pytest, including the PUT tests (existing update, mismatch,
unknown squad number).
Acceptance Criteria
References
Problem
services/player_service.py::update_by_squad_number_asynccopies the incomingPydantic model onto the ORM object one attribute at a time:
Every field addition needs another line here, and a missed line fails silently
(the field just never updates). This is manual property mapping, a .NET/Java
habit. Note that
create_asyncin the same module already uses the idiomaticform —
Player(**player_model.model_dump())— so the two paths areinconsistent.
Proposed Solution
Dump the validated model to a dict and assign with
setattrin a loop.model_dump()uses the Python field names (snake_case), which match the ORMattribute names, so the mapping is 1:1.
The route layer already guarantees
player_model.squad_number == squad_number(400 on mismatch), so including the natural key in the loop is safe.
Suggested Approach
player.squad_number = squad_numberafter the loop (documents that the path parameter is authoritative) or rely
on the route guard — leave a one-line comment either way.
uv run pytest, including the PUT tests (existing update, mismatch,unknown squad number).
Acceptance Criteria
create_async's use ofmodel_dump()CHANGELOG.mdupdatedReferences
setattr— Python docsmodel_dump