Pydantic多继承异常:UserAPI模型未包含自定义_id字段
问题:Pydantic多继承后自定义字段丢失
问题场景
定义了用于请求响应处理的BaseSchema和MongoDB文档配置的BaseDocument,通过继承构建UserAPI模型,期望在UserBase基础上添加_id字段用于API输出,但实际生成的Schema中仅包含UserBase的字段,_id字段缺失。
相关代码:
from datetime import datetime, timezone from typing import Any from beanie import Document from pydash import camel_case from pydantic import BaseModel, Extra def format_datetime_into_isoformat(timestamp: datetime) -> str: return timestamp.replace(tzinfo=timezone.utc).isoformat().replace("+00:00", "Z") def format_dict_key_to_camel_case(key: str) -> str: return camel_case(key) class BaseDocument(BaseModel): class Config: orm_mode: bool = True use_enum_values: bool = True validate_assignment: bool = True allow_population_by_field_name: bool = True json_encoders: dict = {datetime: format_datetime_into_isoformat} class BaseSchema(BaseModel): class Config: extra = Extra.forbid allow_population_by_field_name: bool = True alias_generator: Any = format_dict_key_to_camel_case class UserBase(BaseDocument): user_name: str email: str password: str class User(Document, UserBase): class Settings: name = "users" indexes = ["user_name", "email"] class UserAPI(BaseSchema, UserBase): _id: str
生成的Schema(缺失_id):
{'title': 'UserAPI', 'type': 'object', 'properties': {'userName': {'title': 'Username', 'type': 'string'}, 'email': {'title': 'Email', 'type': 'string'}, 'password': {'title': 'Password', 'type': 'string'}}, 'required': ['userName', 'email', 'password'], 'additionalProperties': False}
原因分析
- 多继承顺序影响字段合并:
UserAPI先继承BaseSchema再继承UserBase,Pydantic的方法解析顺序(MRO)会优先处理BaseSchema的配置,导致子类自定义的_id字段未被正确纳入字段集合。 - 字段别名转换:
BaseSchema中的alias_generator会将_id转换为id,但原代码未意识到这一点,误以为字段完全丢失。
解决方案
方案1:调整继承顺序
将UserBase放在继承顺序首位,确保通用字段先加载,再应用BaseSchema的配置:
class UserAPI(UserBase, BaseSchema): _id: str
此时生成的Schema中,_id会被转换为驼峰命名的id字段,符合API响应的命名规范。
方案2:显式指定字段别名
如果需要保留_id作为响应字段名,可通过Field显式指定别名,避免被alias_generator转换:
from pydantic import Field class UserAPI(BaseSchema, UserBase): _id: str = Field(..., alias="_id")
方案3:修改别名生成规则
在format_dict_key_to_camel_case函数中排除对_id的转换,保留原字段名:
def format_dict_key_to_camel_case(key: str) -> str: if key == "_id": return key return camel_case(key)
验证
调整后执行print(UserAPI.schema()),即可看到包含_id或id字段的完整Schema。
内容的提问来源于stack exchange,提问作者Md Abdur Rakib
相关产品推荐
相关产品推荐

