如何序列化嵌套属性而不显示整个对象?

2024-06-28 11:26:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我的目标如下:

{
  "name": "foo",
  "parent": {"id": "bar", "age": 27}
}

我希望得到以下输出:

{"name": "foo", "parent_id": "bar"}

这是我的Pydantic模型

型号:

class TeacherOutput(PropertyBaseModel):
    name: Optional[str]
    parent_id: Optional[str]

控制器:

@router.get('/teachers', response_model=List[TeacherOutput])
async def get_all():
    teachers = app.model.Teacher.all()
    return teachers

我不知道如何从嵌套对象映射所需的属性。我不需要其他属性


Tags: nameid目标agegetmodel属性foo
2条回答

您可以重写TeacherOutput模型的__init__方法,这样当FastAPI序列化Teacher列表时,您的模型从传入的关键字参数中提取parent['id']

from pydantic import BaseModel
from typing import Dict, List, Optional, Union

class Teacher(BaseModel):
    name: str
    parent: Dict[str, Union[str, int]]

class TeacherOutput(BaseModel):
    name: Optional[str] = None
    parent_id: Optional[str] = None

    def __init__(__pydantic_self__, **kwargs) -> None:
        # print(kwargs)  # ex. {'name': 'John', 'parent': {'id': 'foo', 'age': '24'}}
        parent = kwargs.pop('parent')
        kwargs.update({'parent_id': parent['id']})
        super().__init__(**kwargs)

@app.get('/teachers', response_model=List[TeacherOutput])
async def get_all():
    # teachers = app.model.Teacher.all()
    teachers = [
        Teacher(name='John', parent={
            'id': 'foo',
            'age': 24
        }),
        Teacher(name='Mike', parent={
            'id': 'bar',
            'age': 26
        }),
    ]
    return teachers

通过这种方式,将响应的格式设置与获取实际数据的方式/地点分开。(我假设TeacherOutput确实只是用于响应。)。由此产生的反应将是:

[
    {
        "name": "John",
        "parent_id": "foo"
    },
    {
        "name": "Mike",
        "parent_id": "bar"
    }
]

参见相关阅读资料:

您可以使用的另一种方法(IMHO更干净)是使用多个模型,如Gino的回答所示,但是您可以简单地在path操作函数中转换数据,而不是覆盖__init__函数,如下所示:

from pydantic import BaseModel
from typing import Dict, List, Optional, Union

# I like to specify submodels too with Pydantic to use the dot notation
# but Dict type indicated by Gino works perfectly too 
class TeacherParent(BaseModel):
    id: str
    age: Union[str, int]

class TeacherInput(BaseModel):
    name: str
    parent: TeacherParent

class TeacherOutput(BaseModel):
    name: Optional[str] = None
    parent_id: Optional[str] = None


@router.get('/teachers', response_model=List[TeacherOutput])
async def get_all():
    """Here you still specify the response model for documentation purposes, 
       but transformation happens within the function"""

    teachers = app.model.Teacher.all()
    output = [ 
        TeacherOutput(name=t.name, parent_id=t.parent.id)
        for t in teachers
    ]
    # use parent_id=t.parent['id'] if you didn't specify the parent submodel

    return output

相关问题 更多 >