如何用Pydantic定义动态泛型基类处理Intercom列表模型?
实现泛型Pydantic基类统一处理Intercom列表结构
解决方案思路
Intercom的列表结构有固定模式:包含type字段(格式为{资源单数}.list)和一个与外层键同名的资源列表字段。我们可以通过泛型+元类实现动态生成字段和自动验证,无需重复定义每个列表模型。
完整代码实现
1. 导入依赖
from typing import Generic, TypeVar, List, Type from pydantic import BaseModel, validator, Field from pydantic._internal._model_construction import ModelMetaclass
2. 定义泛型基类与元类
T = TypeVar('T') class BaseListMeta(ModelMetaclass, Generic[T]): def __new__(cls, name, bases, namespace, **kwargs): # 获取泛型参数(列表中的元素类型) item_type = kwargs.get('__parameters__', (None,))[0] if item_type is not None: # 优先使用元素类自定义的资源名称,否则自动推断 if hasattr(item_type, '__resource_name__'): resource_singular = item_type.__resource_name__ else: resource_singular = item_type.__name__.lower() resource_plural = f"{resource_singular}s" # 动态添加资源列表字段 namespace[resource_plural] = Field(..., annotation=List[item_type]) # 动态生成type字段的验证器 def validate_type(cls, v): expected_type = f"{resource_singular}.list" if v != expected_type: raise ValueError(f"Type must be '{expected_type}'") return v namespace['validate_type'] = validator('type')(validate_type) return super().__new__(cls, name, bases, namespace, **kwargs) class BaseList(BaseModel, Generic[T], metaclass=BaseListMeta): type: str class Config: extra = 'forbid' # 禁止多余字段,保证结构严格符合Intercom规范
3. 定义元素模型
class Contact(BaseModel): id: str name: str # 处理不规则复数的示例(如person -> people) class Person(BaseModel): __resource_name__ = "person" # 手动指定资源单数名称 id: str name: str
4. 在父模型中使用
class ParentModel(BaseModel): contacts: BaseList[Contact] people: BaseList[Person]
验证示例
# 符合规范的测试数据 test_data = { "contacts": { "type": "contact.list", "contacts": [ {"id": "1", "name": "Alice"}, {"id": "2", "name": "Bob"} ] }, "people": { "type": "person.list", "people": [ {"id": "3", "name": "Charlie"}, {"id": "4", "name": "Diana"} ] } } # 实例化模型 model = ParentModel(**test_data) print(model.contacts.contacts[0].name) # 输出: Alice print(model.people.people[0].name) # 输出: Charlie # 错误测试(type不符合规范) bad_data = { "contacts": { "type": "contact.lists", # 错误的type "contacts": [{"id": "1", "name": "Alice"}] } } # 会抛出ValidationError: 1 validation error for ParentModel -> contacts -> type # Value must be 'contact.list'
关键说明
- 动态字段生成:元类会根据泛型参数(元素类型)自动生成对应的列表字段(如
Contact对应contacts)。 - 自动类型验证:验证器会自动检查
type字段是否符合{资源单数}.list的格式。 - 不规则复数支持:通过在元素类中定义
__resource_name__属性,可以手动指定资源单数名称,处理如person/people这类不规则复数。
内容的提问来源于stack exchange,提问作者JohnDu17
相关产品推荐
相关产品推荐

