-
Notifications
You must be signed in to change notification settings - Fork 18
Description
Problem
Currently, the API only supports PUT operations for updating player data, which requires sending the entire player object even when only one or two fields need to be modified. This is inefficient and doesn't follow REST best practices for partial resource updates. Users need a way to update specific player fields without having to provide all player data.
Proposed Solution
Implement HTTP PATCH method endpoints to allow partial updates of player resources. The API should accept a PlayerPartialUpdateRequest model and update only the fields provided in the request, leaving other fields unchanged.
Important constraint: Partial updates should allow modifying team-related data (position, squad number, team, league) but NOT personal details (firstName, middleName, lastName, dateOfBirth). Personal information should only be modified via PUT (full update).
Endpoint to implement:
PATCH /players/squadnumber/{squad_number}- Update specific fields of a player by squad number
Note: We use squad number (natural identifier) instead of internal ID, as squad numbers are unique and user-facing.
Suggested Approach
1. Route Definition (routes/player_route.py)
- Add
@api_router.patch("/players/squadnumber/{squad_number}")endpoint - Include cache invalidation (call
await simple_memory_cache.clear(CACHE_KEY)) - Add appropriate status codes (200 for success, 404 for not found, 400 for validation errors, 409 for conflicts)
2. Service Layer (services/player_service.py)
- Create
async def patch_player_by_squad_number(async_session: AsyncSession, squad_number: int, update_data: dict) -> Optional[Player] - If updating squad_number field itself, validate new squad_number doesn't create duplicates
- Handle partial data using
exclude_unset=Truein Pydantic model serialization
3. Model Definition (models/player_model.py)
-
Refactor existing
PlayerModelclass to support Request/Response pattern with data transformations -
Create
PlayerResponseclass (for GET operations) with transformed properties to demonstrate data mapping:class PlayerResponse(BaseModel): full_name: str = Field(alias="fullName") # Computed from firstName + middleName + lastName birth: str # Formatted date string from dateOfBirth dorsal: int # Maps from squadNumber (different naming) position: str club: str # Maps from team (different naming) league: str starting11: str # Transformed from bool to "Yes"/"No" string @field_validator('full_name', mode='before') @classmethod def compute_full_name(cls, v, info): # Compute from firstName, middleName, lastName in entity pass
-
Create
PlayerCreateRequestclass (for POST - all fields except id) -
Create
PlayerUpdateRequestclass (for PUT - full replacement, all fields except id) -
Create
PlayerPartialUpdateRequestclass with only team-related fields as Optional:squad_number: Optional[int]position: Optional[str]abbr_position: Optional[str]team: Optional[str]league: Optional[str]starting11: Optional[bool]- Excludes: firstName, middleName, lastName, dateOfBirth (personal details)
-
Implement mapper function or use Pydantic
@computed_fieldto transform entity to response:- Concatenate firstName, middleName, lastName → fullName
- Format dateOfBirth → birth (ISO date string)
- Rename squadNumber → dorsal
- Rename team → club
- Convert starting11 bool → "Yes"/"No" string
-
All models maintain camelCase aliasing with
to_camelfunction -
Add appropriate validators for fields that require validation (e.g., squad number uniqueness)
4. Testing (tests/test_main.py)
- Test partial updates (single field, multiple fields)
- Test squad number uniqueness validation during PATCH
- Test 404 response for non-existent player
- Test cache invalidation after PATCH operations
- Use test data from
player_stub.py
5. Code Style
- Format with Black:
black . - Lint with flake8:
flake8 - Follow async/await patterns consistently
- Add proper type hints for all function parameters and return values
Acceptance Criteria
- Models refactored to use Request/Response pattern with data transformations:
-
PlayerResponsefor GET operations with computed/transformed fields (fullName, birth, dorsal, club, starting11 as string) - Pydantic computed fields or mapper function transforms entity to response format
-
PlayerCreateRequestfor POST operations -
PlayerUpdateRequestfor PUT operations -
PlayerPartialUpdateRequestfor PATCH operations (excludes personal details)
-
- Data mapping demonstrates Pydantic transformation capabilities
- PATCH endpoint
/players/squadnumber/{squad_number}successfully updates team-related player fields - Personal details (names, date of birth) cannot be modified via PATCH
- Only fields provided in request body are updated (other fields remain unchanged)
- Squad number uniqueness is validated when updating to a new squad number
- Appropriate HTTP status codes returned (200 OK, 404 Not Found, 400 Bad Request, 409 Conflict)
- Cache is invalidated after successful PATCH operations
- All tests pass with
pytest -v - Code coverage remains above 80% target
- Code formatted with Black and passes flake8 linting
- API documentation reflects new model names and PATCH endpoint in
/docs