【问题标题】:How to serialize nested property without showing the whole object?如何序列化嵌套属性而不显示整个对象?
【发布时间】:2021-01-19 11:51:09
【问题描述】:

我有以下对象:

{
  "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

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

【问题讨论】:

  • 什么是PropertyBaseModel
  • @juanpa.arrivillaga 这是使用 pydantic 序列化属性的解决方法,直到 github.com/samuelcolvin/pydantic/issues/935
  • 您能否说明一下您是如何创建TeacherOutput 对象的?
  • @GinoMempin 我正在使用路由器装饰器创建此输出。添加方法

标签: python fastapi pydantic


【解决方案1】:

您可以覆盖TeacherOutput 模型的__init__ 方法,这样当FastAPI 序列化您的Teachers 列表时,您的模型会从传入的关键字参数中提取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"
    }
]

查看相关阅读:

【讨论】:

    【解决方案2】:

    您可以使用的另一种方法(恕我直言)更简洁,是在 Gino 的回答中拥有多个模型,但您可以简单地在路径操作函数中转换数据,而不是覆盖 __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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      • 2019-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多