FastAPI如何区分请求中显式传null和未传递的Optional参数?
解决方案
核心思路是利用Pydantic内置的字段传入记录能力,或者自定义哨兵值区分两种场景,以下是两种可落地的方案:
方案1:使用exclude_unset或model_fields_set(官方推荐)
Pydantic模型实例会自动记录所有用户显式传入的字段,不管字段值是否为null:
- 对于Pydantic V2,可通过实例的
model_fields_set属性获取所有已传入字段的集合 - 对于Pydantic V1,对应的属性为
__fields_set__ - 也可以直接调用
model_dump(exclude_unset=True)直接过滤掉所有未传入的字段,仅保留用户主动提交的内容
示例代码修改
from typing import Optional from fastapi import FastAPI import uvicorn from pydantic import BaseModel class TestEntity(BaseModel): first: Optional[str] second: Optional[str] third: Optional[str] app = FastAPI() @app.post("/test") def test(entity: TestEntity): # 仅获取用户主动传入的字段,包含主动设置为null的字段 update_data = entity.model_dump(exclude_unset=True) # 实际业务场景:拿到update_data后,仅更新这些key对应的数据库字段即可 # 示例返回传入的字段,验证效果 return update_data if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=5000)
效果验证
提交请求{"first":null}后,接口返回结果为:
{ "first": null }
可以准确识别仅first字段为用户主动修改的内容。
方案2:自定义哨兵值区分未传状态
如果需要更明确的类型标注,也可以自定义一个唯一的哨兵值作为字段的默认值,和null做区分:
from typing import Optional, Any from fastapi import FastAPI import uvicorn from pydantic import BaseModel # 定义唯一的未传标记哨兵 UNSET: Any = object() class TestEntity(BaseModel): first: Optional[str] | type[UNSET] = UNSET second: Optional[str] | type[UNSET] = UNSET third: Optional[str] | type[UNSET] = UNSET app = FastAPI() @app.post("/test") def test(entity: TestEntity): update_data = {} for field_name in ["first", "second", "third"]: field_value = getattr(entity, field_name) if field_value is not UNSET: # 只要不是UNSET,不管是None还是正常值都属于用户主动传入 update_data[field_name] = field_value return update_data if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=5000)
该方案和方案1效果完全一致,适合需要在类型层面明确区分未传状态的场景。
内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

