1.8 KiB
1.8 KiB
| title | description | tags | created | updated | projects | |||||
|---|---|---|---|---|---|---|---|---|---|---|
| Pydantic Empty String → None Coercion | field_validator with mode='before' to treat empty string as absent optional field, preventing 422/400 on CC-only or AD-only payloads |
|
2026-05-01 | 2026-05-01 |
|
Pydantic Empty String → None Coercion
Problem
Frontend forms often send "" for optional fields that were not filled in. Pydantic str | None fields accept "" as a valid non-None string, so downstream guards like if request.audio_description_vtt: silently skip validation — but then attempt VTT format parsing on an empty string, producing confusing 400 errors.
Concrete bug (video-accessibility): CC-only jobs sent audio_description_vtt: "". The PATCH /vtt handler tried to validate the empty string as a VTT file and rejected it with 400 instead of ignoring it.
Solution
from typing import Any
from pydantic import BaseModel, field_validator
class VttUpdateRequest(BaseModel):
captions_vtt: str | None = None
audio_description_vtt: str | None = None
@field_validator('captions_vtt', 'audio_description_vtt', mode='before')
@classmethod
def empty_str_to_none(cls, v: Any) -> str | None:
return None if v == '' else v
mode='before' runs before Pydantic's own type coercion, so the empty string is converted to None before field assignment. After this, if request.audio_description_vtt: correctly evaluates to False.
Notes
- Works for any
str | Nonefield where""should mean "not provided" - Python 3.10+ syntax (
str | None); useOptional[str]+from __future__ import annotationsfor older versions - Ruff UP007 flags
Optional[str]— preferstr | None - Apply to all optional VTT / text content fields in upload/edit schemas