I'm building a FastAPI application and modeling a forum-style comment system using Pydantic v2. Each comment can contain replies, and those replies can contain more replies, recursively, similar to Reddit or GitHub threads.
A simplified version of my model (without user) looks like this:
from __future__ import annotations from pydantic import BaseModel from typing import List class Comment(BaseModel): id: int text: str replies: List[Comment] | None = None Comment.model_rebuild() This works fine for basic validation. However, I need to enforce a rule:
The comment tree must not exceed a maximum nesting depth. For example, maximum depth = 3. (So it doesn't get's ugly or breaks the UI). So with the payload:
{ "id": 1, "text": "Level 1", "replies": [ { "id": 2, "text": "Level 2", "replies": [ { "id": 3, "text": "Level 3", "replies": [ { "id": 4, "text": "Level 4 INVALID" } ] } ] } ] } I want Pydantic to raise a validation error because the depth exceeds the allowed maximum.
What I've Tried
I attempted to compute the depth inside a field_validator, but since the model validates recursively without knowing its own call depth, I hit issues like RecursionError or inability to access context about the current recursion level, or even validators running before all children are built.
from __future__ import annotations from pydantic import BaseModel, field_validator, ValidationError MAX_DEPTH = 3 class Comment(BaseModel): id: int text: str replies: list[Comment] | None = None @field_validator("replies", mode="after") def validate_depth(cls, replies): if replies: depth = cls._compute_depth(replies) if depth > MAX_DEPTH: raise ValueError(f"Maximum depth {MAX_DEPTH} exceeded (found: {depth})") return replies @classmethod def _compute_depth(cls, replies): if not replies: return 1 return 1 + max(cls._compute_depth(r.replies) for r in replies) Comment.model_rebuild() This works in BASIC cases and i want to findthe best approach, or is there a more idiomatic or built-in way in Pydantic v2 to handle recursive validation with depth constraints?