obsidian/wiki/tech-patterns/pydantic-empty-string-coercion.md
2026-05-01 12:15:13 +01:00

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
pydantic
fastapi
python
validation
2026-05-01 2026-05-01
video-accessibility

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 | None field where "" should mean "not provided"
  • Python 3.10+ syntax (str | None); use Optional[str] + from __future__ import annotations for older versions
  • Ruff UP007 flags Optional[str] — prefer str | None
  • Apply to all optional VTT / text content fields in upload/edit schemas