55 lines
1.8 KiB
Markdown
55 lines
1.8 KiB
Markdown
---
|
|
id: 530bfb6f-841c-4381-b8a4-d0b6fbc02878
|
|
type: fix
|
|
title: "Fix: Pydantic bare default evaluated at class definition time (paper-dynasty-database)"
|
|
tags: [paper-dynasty, python, pydantic, fix, bug, fastapi]
|
|
importance: 0.65
|
|
confidence: 0.8
|
|
created: "2026-03-03T20:06:06.122074+00:00"
|
|
updated: "2026-03-03T20:06:06.763300+00:00"
|
|
relations:
|
|
- target: 04e57a23-0a20-49d6-8c5a-2fa5fc4e55b5
|
|
type: RELATED_TO
|
|
direction: outgoing
|
|
strength: 0.7
|
|
edge_id: b210b16d-b168-4798-b73f-98ef27f46dd8
|
|
- target: d36a86f0-8183-4c94-8d63-0be65d3fd63a
|
|
type: RELATED_TO
|
|
direction: outgoing
|
|
strength: 0.68
|
|
edge_id: 62213d38-b5b9-4166-8d1b-4b48f216772a
|
|
- target: 5ae1bf6e-1e1c-4486-8a4d-10afd9e42189
|
|
type: RELATED_TO
|
|
direction: outgoing
|
|
strength: 0.68
|
|
edge_id: b3e61cf3-d61d-4421-8e81-8818a7601791
|
|
---
|
|
|
|
## Problem
|
|
|
|
`PlayerModel.offense_col` in `app/routers_v2/mlbplayers.py` used a bare `random.randint(1, 3)` as the Pydantic field default. Python evaluates this expression once at class definition time (module import), so every instance shared the same random integer for the entire process lifetime.
|
|
|
|
## Root Cause
|
|
|
|
Pydantic field defaults that are not wrapped in `Field(default_factory=...)` are evaluated eagerly at class body execution time — identical to how Python function default argument values work. This is a common footgun with mutable or randomised defaults.
|
|
|
|
## Solution
|
|
|
|
```python
|
|
# Before (broken)
|
|
offense_col: int = random.randint(1, 3)
|
|
|
|
# After (correct)
|
|
offense_col: int = pydantic.Field(default_factory=lambda: random.randint(1, 3))
|
|
```
|
|
|
|
`default_factory` is called on each model instantiation, producing a fresh value per request.
|
|
|
|
## Files Changed
|
|
|
|
- `app/routers_v2/mlbplayers.py` line 40
|
|
|
|
## References
|
|
|
|
- Issue #24, PR #38 in cal/paper-dynasty-database
|