【问题标题】:How to exclude Optional unset values from a Pydantic model using FastAPI?如何使用 FastAPI 从 Pydantic 模型中排除可选的未设置值?
【发布时间】:2023-02-10 15:51:28
【问题描述】:

我有这个模型:

class Text(BaseModel):
    id: str
    text: str = None


class TextsRequest(BaseModel):
    data: list[Text]
    n_processes: Union[int, None]

所以我希望能够接受这样的请求:

{"data": ["id": "1", "text": "The text 1"], "n_processes": 8} 

{"data": ["id": "1", "text": "The text 1"]}.

现在在第二种情况下我得到

{'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None}

使用此代码:

app = FastAPI()

@app.post("/make_post/", response_model_exclude_none=True)
async def create_graph(request: TextsRequest):
    input_data = jsonable_encoder(request)

那么我怎样才能在这里排除n_processes呢?

【问题讨论】:

    标签: python fastapi pydantic


    【解决方案1】:

    您可以使用 Pydantic 的 model.dict(...)exclude_none 参数:

    class Text(BaseModel):
        id: str
        text: str = None
    
    
    class TextsRequest(BaseModel):
        data: list[Text]
        n_processes: Optional[int]
    
    
    request = TextsRequest(**{"data": [{"id": "1", "text": "The text 1"}]})
    print(request.dict(exclude_none=True))
    

    输出:

    {'data': [{'id': '1', 'text': 'The text 1'}]}
    

    此外,写Optional[int]而不是Union[int, None]更为惯用。

    【讨论】:

    • 不同意你最后的说法。 Union 比 Optional 更接近 python 3.10 语法,但这只是我的看法。
    【解决方案2】:

    Pydantic 为使用 model.dict(...) 方法导出模型提供了以下参数:

    exclude_unset: 时是否明确设置了哪些字段 创建模型应该从返回的字典中排除; 默认False

    exclude_none:等于 None 的字段是否应该 从返回的字典中排除;默认False

    由于您指的是排除可选取消设置参数,您可以使用第一种方法(即exclude_unset)。这在想要排除参数时很有用仅有的如果它没有被设置为某个值或None

    但是,exclude_none 参数忽略了一个属性可能已被有意设置为 None 的事实,因此将其从返回的字典中排除。

    例子:

    from pydantic import BaseModel
    from typing import List, Union
    
    class Text(BaseModel):
        id: str
        text: str = None
    
    class TextsRequest(BaseModel):
        data: List[Text]    # in Python 3.9+ you can use:  data: list[Text]
        n_processes: Union[int, None] = None
    
    t = TextsRequest(**{'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None})
    print(t.dict(exclude_none=True))
    #> {'data': [{'id': '1', 'text': 'The text 1'}]}
    print(t.dict(exclude_unset=True))
    #> {'data': [{'id': '1', 'text': 'The text 1'}], 'n_processes': None}
    

    关于可选参数

    使用Union[int, None] 与使用Optional[int] 相同(两者等价)。然而,使参数可选的最重要部分是 = None 部分。

    根据FastAPI documentation(见警告笔记信息在提供的链接中):

    笔记

    FastAPI 会知道 q 的值不是必需的,因为 默认值= None

    Union[str, None] 中的 Union 将允许您的编辑给您 更好的支持和检测错误。

    信息

    请记住,使参数可选的最重要的部分 是部分:= None,因为它将使用 None 作为默认值,这样就可以 范围不需要.

    Union[str, None] 部分允许您的编辑器提供更好的 支持,但这不是告诉 FastAPI 的原因这个参数是不需要.

    因此,无论您选择使用哪个选项,如果后面没有 = None 部分,FastAPI 将不知道参数的值是选修的,因此,用户将必须提供一些价值。还可以通过 http://127.0.0.1:8000/docs 上的自动生成的 API 文档进行检查,其中 parameterrequest body 将显示为Required场地。

    例如,以下任何一项都会要求用户在他们对TextsRequest模型的请求中传递一些body内容:

    @app.post("/upload")
    def upload(t: Union[TextsRequest, None]):
        pass
    
    @app.post("/upload")
    def upload(t: Optional[TextsRequest]):
        pass
    

    但是,如果上述TextsRequest定义是继之后= None,例如:

    @app.post("/upload")
    def upload(t: Union[TextsRequest, None] = None):
        pass
    
    @app.post("/upload")
    def upload(t: Optional[TextsRequest] = None):
        pass
        
    @app.post("/upload")
    def upload(t: TextsRequest = None): # this should work as well
        pass
    

    参数(或主体)将是选修的,因为 = None 会告诉 FastAPI 这个参数是不需要.

    在 Python 3.10+

    good news是在Python 3.10及以上版本中,你不必担心像OptionalUnion这样的名称,因为你可以简单地使用竖线|(也称为按位或运算符)来定义一个选修的参数(或简单地说,类型的联合)。然而, 同样的规则也适用于这个选项,即如果你想使参数,你仍然需要添加 = None 部分选修的(如以下示例所示)。

    例子:

    @app.post("/upload")
    def upload(t: TextsRequest | None = None):
        pass
    

    【讨论】:

      猜你喜欢
      • 2021-01-22
      • 2022-01-21
      • 2021-03-04
      • 2021-10-16
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 2023-02-23
      相关资源
      最近更新 更多