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?