简体   繁体   English

如何使用 FastAPI 返回包含不同 Pydantic 模型列表的响应?

[英]How to return a response with a list of different Pydantic models using FastAPI?

Context语境

I am creating an API using FastAPI to compute the shortest path on a graph.我正在使用 FastAPI 创建一个 API 来计算图形上的最短路径。 My response consists of nodes and relations.我的回应由节点和关系组成。

These nodes can be of different types, which means they can have different attributes:这些节点可以是不同的类型,这意味着它们可以具有不同的属性:

class SchoolNode(BaseModel):
    uid: int
    code: Optional[str]
    label: str = 'school'


class PersonNode(BaseModel):
    uid: int
    name: Optional[str]
    surname: Optional[str]
    label: str = 'person'


class PetNode(BaseModel):
    uid: int
    name: Optional[str]
    surname: Optional[str]
    label: str = 'pet'

My response follows this format:我的回复遵循以下格式:

class Response(BaseModel):
    links: List[Link]
    nodes: List[Union[SchoolNode, PersonNode, PetNode]]
    start: int
    end: int

Note that I cannot change the response format since my output will be used by a custom library based on d3js that needs data in input in this specific format.请注意,我无法更改响应格式,因为我的 output 将由基于 d3js 的自定义库使用,该库需要以这种特定格式输入数据。

You can see a gist with full code example here .您可以在此处查看包含完整代码示例的要点。

Problem问题

The API run successfully, but the response inside 'nodes' property is unable to understand which model must be chosen. API 成功运行,但“节点”属性内的响应无法理解必须选择哪个 model。 The expected output is:预期的 output 是:

{
    'links': [
        {'source': 1, 'target': 123, 'type': 'GO_TO_SCHOOL'},
        {'source': 100, 'target': 123, 'type': 'GO_TO_SCHOOL'},
    ],
    'nodes': [
        {'uid': 1, 'label': 'person', 'name': 'Bob', 'surname': 'Foo'},
        {'uid': 123, 'label': 'school', 'code': 'ABCD'},
        {'uid': 100, 'label': 'person', 'name': 'Alice', 'surname': 'Bar'}
    ],
    'start': 1,
    'end': 100
}

while the obtained output is:而得到的output是:

{
    "links": [
        {"source": 1, "target": 123, "type": "GO_TO_SCHOOL"},
        {"source": 123, "target": 100, "type": "GO_TO_SCHOOL"}
    ],
    "nodes": [
        {"uid": 1, "code": null, "label": "person"},
        {"uid": 123, "code": "ABCD", "label": "school"},
        {"uid": 100, "code": null, "label": "person"}
    ],
    "start": 1,
    "end": 100
}

Here you can see how the first and third nodes show the attributes of the first node (SchoolNode) instead of the correct ones (PersonNode)在这里您可以看到第一个和第三个节点如何显示第一个节点 (SchoolNode) 的属性而不是正确的属性 (PersonNode)

Question问题

How should I change my Response to return the correct output?我应该如何更改我的响应以返回正确的 output? I tried using an if-then-else logic like我尝试使用类似 if-then-else 的逻辑

nodes = []
for node in graph['nodes']:
    if node['label'] == 'person':
        node.append(PersonNode(**node)
    elif:
        ...

but nothing changed.但没有任何改变。

I also tried using Field(..., discriminator='label') and I guess this is the correct way to address this issue but without success at the moment.我也尝试使用Field(..., discriminator='label')我想这是解决这个问题的正确方法,但目前没有成功。

Any help is appreciated, thanks in advance!感谢您的帮助,在此先感谢!

Thanks to @ Chris and following the links he sent me I can solve this problem.感谢@Chris并点击他发给我的链接,我可以解决这个问题。

The solution was creating a unique model UnionNode with a __root__ property with Field(..., discriminator='label') .解决方案是创建一个独特的 model UnionNode ,其__root__属性带有Field(..., discriminator='label') Moreover, label property in nodes must have a Literal typing.此外,节点中的 label 属性必须具有Literal类型。

class SchoolNode(BaseModel):
    id: int
    label: Literal['school']
    code: Optional[str]


class PersonNode(BaseModel):
    id: int
    label: Literal['person']
    name: Optional[str]
    surname: Optional[str]


class PetNode(BaseModel):
    id: int
    label: Literal['pet']
    name: Optional[str]
    surname: Optional[str]


class UnionNode(BaseModel):
    __root__: Union[SchoolNode, PersonNode, PetNode] = Field(..., discriminator='label')


class Response(BaseModel):
    links: List[Link]
    nodes: List[UnionNode]
    start: int
    end: int

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM