Definining recursive models in Pydantic?

Viewed 3933

How can I define a recursive Pydantic model?

Here's an example of what I mean:

from typing import List
from pydantic import BaseModel

class Task(BaseModel):
    name: str
    subtasks: List[Task] = []

but when I run that I get the following error:

NameError                                 Traceback (most recent call last)
<ipython-input-1-c6dca1d390fe> in <module>
      2 from pydantic import BaseModel
      3
----> 4 class Task(BaseModel):
      5     name: str
      6     subtasks: List[Task] = []

<ipython-input-1-c6dca1d390fe> in Task()
      4 class Task(BaseModel):
      5     name: str
----> 6     subtasks: List[Task] = []
      7

NameError: name 'Task' is not defined

I looked through the documentation but couldn't find anything. For example, at the page on "Recursive Models", but it seems to be about nesting subtypes of BaseModel not about a recursive type definition.

Thanks for your help!

3 Answers

Either put from __future__ import annotations at the top of the file, or change your annotation to List['Task'].

The relevant pydantic documentation is here: https://pydantic-docs.helpmanual.io/usage/postponed_annotations/

But the error in your question is not pydantic specific, that's just how type annotations work.

Expanding on the accepted answer from Alex Hall:

From the Pydantic docs, it appears the call to update_forward_refs() is still required whether or not annotations is imported.

from typing import List
from pydantic import BaseModel

class Task(BaseModel):
    name: str
    subtasks: List['Task'] = []


Task.update_forward_refs()

or

# python3.7+
from __future__ import annotations
from typing import List
from pydantic import BaseModel

class Task(BaseModel):
    name: str
    subtasks: List[Task] = []


Task.update_forward_refs()

https://pydantic-docs.helpmanual.io/usage/postponed_annotations/#self-referencing-models

I found myself in a situation in which I want to run a root validator on the class. What happens is that, although the above solution seem to work find in some cases, the value of the recursive field subtasks doesn't show up in the values dictionary parameter of the @root_validator.

I ended up with declaring

subtasks: List[Dict]

and recursively build the subtasks objects in the @root_validator:

subtasks = values.get('subtasks')
if subtasks:
    values['subtasks']=[Task(**subtask) for subtask in subtasks]
Related