FastAPI中如何配置BaseModel,确保请求体至少包含指定字段之一
解决FastAPI BaseModel至少包含一个字段的验证需求
要实现“请求体必须包含investigation、dataset、datafile中至少一个字段,单个字段非必填”的需求,可以通过Pydantic的自定义模型验证器来实现,以下分Pydantic v2和v1两种主流版本给出解决方案:
Pydantic v2 方案
在Mint类中添加model_validator,在字段基础验证完成后检查是否存在至少一个有效字段:
from pydantic import BaseModel, model_validator, ValidationError from typing import Union, List # 假设已定义以下模型 class Investigation(BaseModel): id: int class Dataset(BaseModel): name: str class Datafile(BaseModel): path: str class Mint(BaseModel): investigation: Union[Investigation, None] = None dataset: Union[List[Dataset], None] = None datafile: Union[List[Datafile], None] = None @model_validator(mode='after') def check_at_least_one_field(self) -> 'Mint': # 检查各字段是否存在有效值 has_valid_investigation = self.investigation is not None has_valid_dataset = self.dataset is not None and len(self.dataset) > 0 has_valid_datafile = self.datafile is not None and len(self.datafile) > 0 if not (has_valid_investigation or has_valid_dataset or has_valid_datafile): raise ValueError("请求体必须包含investigation、dataset或datafile中的至少一个,且dataset/datafile不能为空列表") return self
验证逻辑说明
mode='after'确保在所有字段的格式验证完成后再执行此检查,避免先判断空值再验证格式的混乱。- 针对
dataset和datafile,额外判断列表长度大于0,符合你“一个或多个”的接收要求。 - 若所有字段都无有效值,抛出
ValueError,FastAPI会自动将其转换为422验证错误返回给客户端。
Pydantic v1 方案
如果你的项目仍在使用Pydantic v1,可使用@validator实现相同逻辑:
from pydantic import BaseModel, validator, ValidationError from typing import Union, List class Investigation(BaseModel): id: int class Dataset(BaseModel): name: str class Datafile(BaseModel): path: str class Mint(BaseModel): investigation: Union[Investigation, None] = None dataset: Union[List[Dataset], None] = None datafile: Union[List[Datafile], None] = None @validator('*', pre=False, always=True) def check_at_least_one_field(cls, v, values): has_valid_investigation = values.get('investigation') is not None has_valid_dataset = values.get('dataset') is not None and len(values.get('dataset')) > 0 has_valid_datafile = values.get('datafile') is not None and len(values.get('datafile')) > 0 if not (has_valid_investigation or has_valid_dataset or has_valid_datafile): raise ValueError("请求体必须包含investigation、dataset或datafile中的至少一个,且dataset/datafile不能为空列表") return v
验证逻辑说明
*表示对所有字段生效,always=True确保即使所有字段都使用默认值(None)也会触发验证。pre=False表示在字段自身的格式验证完成后执行此检查。
测试示例
有效请求(验证通过)
# 仅包含investigation mint1 = Mint(investigation=Investigation(id=1)) # 包含investigation和2个dataset mint2 = Mint(investigation=Investigation(id=1), dataset=[Dataset(name="dataset1"), Dataset(name="dataset2")]) # 仅包含2个dataset mint3 = Mint(dataset=[Dataset(name="dataset1"), Dataset(name="dataset2")]) # 仅包含4个datafile mint4 = Mint(datafile=[Datafile(path="/file1.txt"), Datafile(path="/file2.txt"), Datafile(path="/file3.txt"), Datafile(path="/file4.txt")])
无效请求(触发验证错误)
# 所有字段为空 try: mint5 = Mint() except ValidationError as e: print(e) # 抛出验证错误 # dataset为空列表 try: mint6 = Mint(dataset=[]) except ValidationError as e: print(e) # 抛出验证错误
内容的提问来源于stack exchange,提问作者WhatTheWhat
相关产品推荐
相关产品推荐

