You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 18:28:01