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

FastAPI与ODMantic嵌套模型验证时触发'string indices must be integers, not str'错误的解决方案咨询

FastAPI与ODMantic嵌套模型验证时触发'string indices must be integers, not str'错误的解决方案咨询

嗨,我来帮你排查这个问题~

首先,这个错误的根源其实很明确:你的字段验证器在嵌套模型还没被解析成对象的时候,就尝试用属性访问的方式去操作它了。

在Pydantic v2中,@field_validator默认是在字段完成解析之前执行的(也就是before=True的默认行为)。这时候contact_details还只是原始的输入字典,而不是FoundContactDetails实例——你尝试用value.address.street这种属性访问方式去操作字典,自然会触发string indices must be integers, not 'str'的错误,因为字典只能通过['key']的方式访问内容,不能用.。

给你几个可行的解决方案,按推荐程度排序:


方案1:调整验证器的执行时机(最推荐)

在@field_validator装饰器里加上mode='after'参数,让Pydantic先把contact_details完整解析成FoundContactDetails实例,再执行你的验证逻辑。这时候你用.访问属性就完全没问题了:

class FoundFormCreate(FoundFormBase):
    @field_validator('contact_details', mode='after')
    def validate_contact_details(cls, value):
        if value.bank_account is not None and value.bank_name is None:
            raise ValueError('Bank name is required if bank account is provided')
        if value.address is not None:
            # 此时value.address已经是DetailedAddress实例,可正常用.访问属性
            if not all([
                value.address.street, 
                value.address.ward, 
                value.address.district, 
                value.address.city, 
                value.address.country
            ]):
                raise ValueError('All address fields (street, ward, district, city, country) are required if address is provided')
        return value

方案2:利用Pydantic自身的嵌套验证简化逻辑(更优雅)

仔细看你的DetailedAddress模型,所有字段都是必填的str类型,没有设置Optional。这意味着Pydantic会自动帮你验证:如果用户提交了address,就必须提供所有字段,否则会直接抛出缺少字段的错误——完全不需要你手动写all([...])的检查逻辑。

这样你可以把验证器简化成只处理银行账户和银行名称的关联逻辑:

class FoundFormCreate(FoundFormBase):
    @field_validator('contact_details', mode='after')
    def validate_contact_details(cls, value):
        if value.bank_account is not None and value.bank_name is None:
            raise ValueError('Bank name is required if bank account is provided')
        # address的完整性由DetailedAddress模型自动验证,无需额外代码
        return value

这种方式既简洁又不容易出错,把字段级的验证交给Pydantic原生处理,你只需要处理跨字段的关联规则就行。


方案3:直接操作原始字典(不推荐,仅作参考)

如果你非要保持验证器的默认执行时机,那可以改成用字典的键访问方式来检查原始输入数据,但这种代码可读性较差,不推荐:

class FoundFormCreate(FoundFormBase):
    @field_validator('contact_details')
    def validate_contact_details(cls, value):
        # 此时value是原始输入字典,不是模型实例
        if value.get('bank_account') is not None and value.get('bank_name') is None:
            raise ValueError('Bank name is required if bank account is provided')
        address = value.get('address')
        if address is not None:
            if not all([
                address.get('street'), 
                address.get('ward'), 
                address.get('district'), 
                address.get('city'), 
                address.get('country')
            ]):
                raise ValueError('All address fields (street, ward, district, city, country) are required if address is provided')
        return value

试一下方案1或者方案2,应该就能解决你遇到的问题了~

备注:内容来源于stack exchange,提问作者Quang Truong Nguyen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:57:59