mcpField
P
pydantic-v2

pydantic-v2

github.com/bm629开发6117 次安装1 周前更新
代码仓库

关于 pydantic-v2

面向 Python 开发者的 Pydantic v2 参考技能,帮助编写和验证数据模型。内容聚焦 v2 API、序列化及 v1 迁移时的常见问题。

主要功能

  1. 01类型化模型与字段约束
  2. 02字段及模型验证器
  3. 03序列化与计算字段
  4. 04从环境变量和 .env 加载设置
  5. 05TypeAdapter 与联合类型验证
  6. 06v1 到 v2 迁移指导

适用场景

  1. 01定义经过验证的 DTO 和配置模型
  2. 02添加字段或跨字段自定义验证
  3. 03序列化模型以供 API 或数据交换使用
  4. 04从环境变量加载应用设置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bm629/agent-skills --skill pydantic-v2

安装后在 Claude Code 中输入/pydantic-v2确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
migration-v1-to-v2.md
serialization.md
settings.md
sources.md
validators.md

详细介绍

Pydantic v2

概述

Pydantic v2 根据 Python 类型提示验证并序列化数据。BaseModel 的子类通过注解声明字段;实例化时会验证并转换输入,并可通过 model_dump* 将实例 往返转换为字典/JSON。本技能介绍编写当前 v2 代码所需的知识增量——即 v1 之后的 API(model_config = ConfigDict(...)、model_dump()、@field_validator),以及 v1 编码习惯遗留后容易踩到的陷阱。它涵盖独立使用 Pydantic 的场景:模型、字段、验证器、 序列化、配置、pydantic-settings、TypeAdapter,以及结构化类型和联合类型。关于框架层面的请求/响应 建模和 response_model,请参阅 fastapi 技能——本技能不重复介绍 框架机制。

何时启用

  • ✅ 使用带类型的字段和约束定义数据模型、配置对象或 DTO。
  • ✅ 编写字段级或模型级验证、自定义序列化,或计算字段。
  • ✅ 使用 pydantic-settings 从环境变量 / .env 加载应用配置。
  • ✅ 使用 TypeAdapter 批量验证非模型形状(list[Model]、字典、基本类型)。
  • ✅ 将 v1 时代的代码(class Config、.dict()、@validator)现代化为 v2。

以下情况请勿启用:

  • 任务涉及 FastAPI 请求/响应接线或 response_model——请使用 fastapi 技能。
  • 主题是 Pydantic-AI Agent 或 Logfire 插桩——它们属于不同的产品,不在范围内。

工作流

步骤 1:使用类型提示定义模型

字段的写法是 name: Type。没有默认值时,字段为必填;设置默认值后,字段才是 可选的。特别注意,在 v2 中,Optional[int](即 int | None)只表示字段可为 空——它不会提供默认值;省略默认值时,该字段仍为必填。对可变默认值使用 default_factory(不要使用字面量 []/{},否则多个实例会共享同一个值)。

from pydantic import BaseModel, Field

class User(BaseModel):
    id: int                                   # required
    name: str = "anon"                        # optional, has default
    nickname: str | None = None               # nullable AND optional (explicit default)
    tags: list[str] = Field(default_factory=list)  # safe mutable default

使用可信的关键字参数构造(User(id=1)),或者使用 类方法 User.model_validate(obj)(字典 / 对象)和 User.model_validate_json(data)(JSON 字符串/字节)验证不可信输入。 model_construct(**data) 会跳过验证——仅用于可信数据。

步骤 2:使用 Annotated[..., Field(...)] 约束字段

优先使用 Annotated 风格;它能保持类型准确,并便于组合。 Field 提供 gt/ge/lt/le/multiple_of(数值)、 min_length/max_length/pattern(字符串),以及 default、default_factory、 description、alias 和 exclude。

from typing import Annotated
from pydantic import BaseModel, Field

class Account(BaseModel):
    balance: Annotated[int, Field(ge=0)]
    handle: Annotated[str, Field(min_length=3, max_length=32, pattern=r"^[a-z0-9_]+$")]
    score: Annotated[float, Field(default=0.0, le=100.0)]
步骤 3:使用 @field_validator / @model_validator 验证

字段验证器按字段运行;模型验证器则跨字段运行。关于 before/after/ plain/wrap 模式、ValidationInfo 和 @classmethod 规则,请参阅 references/validators.md。

from typing_extensions import Self
from pydantic import BaseModel, field_validator, model_validator

class Signup(BaseModel):
    email: str
    password: str
    password_repeat: str

    @field_validator("email", mode="after")
    @classmethod
    def lowercase(cls, v: str) -> str:
        return v.lower()

    @model_validator(mode="after")
    def passwords_match(self) -> Self:
        if self.password != self.password_repeat:
            raise ValueError("passwords do not match")
        return self
步骤 4:使用 model_dump* 序列化

model_dump() → 字典,model_dump_json() → JSON 字符串。使用 include / exclude / exclude_unset / exclude_defaults / exclude_none / by_alias / mode 控制输出结构。使用 @field_serializer 自定义序列化,并通过 @computed_field 添加派生输出。请参阅 references/serialization.md。

user.model_dump()                              # dict, python types
user.model_dump(exclude={"password"})          # drop a field
user.model_dump(exclude_unset=True)            # only explicitly-set fields (PATCH-style)
user.model_dump_json(by_alias=True, exclude_none=True, indent=2)
步骤 5:使用 model_config = ConfigDict(...) 配置

类属性 model_config(而非嵌套的 class Config)用于存放 ConfigDict。常用选项:strict、from_attributes(读取 ORM 对象)、 validate_assignment、use_enum_values、extra("ignore"/"allow"/ "forbid")、frozen、str_strip_whitespace。对于按别名验证的配置,当前 2.11+ 更推荐使用 validate_by_name=True(并同时设置 validate_by_alias=True)——请参阅“易踩陷阱”。

from pydantic import BaseModel, ConfigDict

class Row(BaseModel):
    model_config = ConfigDict(from_attributes=True, extra="forbid", frozen=True)
    id: int
    label: str

obj = Row.model_validate(orm_row)   # from_attributes reads attributes off an ORM object
步骤 6:使用 pydantic-settings 加载配置

BaseSettings 位于独立的 pydantic-settings 包中(不属于核心 pydantic)。它会从环境变量和 .env 中读取字段。通过 SettingsConfigDict 配置。有关嵌套设置和来源优先级,请参阅 references/settings.md。

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="APP_", env_file=".env",
                                      env_file_encoding="utf-8")
    api_key: str                  # required → from APP_API_KEY
    host: str = "127.0.0.1"
    port: int = 8000              # coerced from the string env value

settings = Settings()             # reads env / .env at construction
步骤 7:结构——枚举、Literal、联合类型、TypeAdapter

将一个模型用作另一个模型的字段类型,以嵌套模型。对于固定选项,使用 Enum(继承 str, Enum,以便生成适合 JSON 的值)或 Literal。 判别式联合类型会通过 Field(discriminator=...) 根据标签字段进行分派。 使用 TypeAdapter 验证非模型形状(列表、字典、基本类型)——对于批量或独立验证, 这是比 RootModel 更符合 v2 惯用方式的选择。

from typing import Annotated, Literal
from pydantic import BaseModel, Field, TypeAdapter

class Cat(BaseModel):
    kind: Literal["cat"]
    lives: int

class Dog(BaseModel):
    kind: Literal["dog"]
    good_boy: bool = True

class Owner(BaseModel):
    pet: Annotated[Cat | Dog, Field(discriminator="kind")]

cats = TypeAdapter(list[Cat]).validate_python([{"kind": "cat", "lives": 9}])
步骤 8:处理 ValidationError

输入无效时,model_validate* 和模型构造会引发 ValidationError。 .errors() 返回一个字典列表(type、loc、msg、input、url); .error_count() 返回数量;.json() 会将其序列化。

from pydantic import ValidationError

try:
    User.model_validate({"id": "not-an-int"})
except ValidationError as e:
    for err in e.errors():
        print(".".join(map(str, err["loc"])), err["type"], err["msg"])

规则

硬性规则(绝不违反):

  • 仅使用 v2 API。 model_config = ConfigDict(...)、model_dump() / model_dump_json()、model_validate() / model_validate_json()、 @field_validator / @model_validator、model_json_schema()。 绝不使用 class Config、.dict() / .json()、@validator / @root_validator、 .parse_obj() / .parse_raw() 或 .schema()。
  • @field_validator 下方必须紧接 @classmethod。 @model_validator(mode="after") 对 self 操作,不需要 @classmethod。
  • Optional[x] 不会提供默认值。 如果字段应为可选字段,请显式提供默认值(x | None = None)。
  • 可变默认值必须使用 default_factory, 绝不能直接使用 [] / {} / dict()。
  • BaseSettings 从 pydantic_settings 导入, 而不是 pydantic。
  • 不要在 BaseModel 中将 TypeAdapter 用作字段注解; 应单独使用它来验证非模型类型。

偏好(可覆盖):

  • 优先使用 Annotated[T, Field(...)],而不是将 Field(...) 赋值为默认值。
  • 将请求模型和响应模型分开,不要用同一个模型处理两者。
  • 对高频路径复用同一个 TypeAdapter 实例(构建一次,多次验证)。

注意事项

  • populate_by_name 即将弃用。 在 Pydantic 2.11+ 中,优先使用 ConfigDict(validate_by_name=True, validate_by_alias=True)。populate_by_name 在 2.x 中仍可用(计划在 v3 中弃用)——旧版 2.x 可以继续使用,但当前版本应写 新的这两个配置项。
  • class Config 虽然仍会静默生效,但已弃用——它不会报错,因此 v1 代码“看起来没问题”, 实际上却不符合规范,并且无法使用 v2 选项。请改为 model_config = ConfigDict(...)。
  • 在 @field_validator 上忘记加 @classmethod 会在类定义时引发错误,或导致行为异常—— 装饰器顺序应为顶部的 @field_validator(...),其下方紧接 @classmethod。
  • 将 model_config 写成普通 dict 虽然可用,但会失去类型检查器的辅助提示——请使用 ConfigDict(...) 构造函数,以便 IDE 和 ty/mypy 验证键名。
  • 宽松强制转换可能带来意外。 默认情况下,"123" 会被强制转换为 123。如果需要 精确匹配,请设置 ConfigDict(strict=True)(或针对每个字段设置 Field(strict=True))。
  • exclude_unset 与 exclude_defaults 的区别。 exclude_unset 会排除调用方从未设置的字段 (非常适合 PATCH 请求体);exclude_defaults 会排除值等于默认值的字段,即使该字段已设置。 两者不可互换。

反模式

  • “这只是个小模型,我就用旧的 .dict() 吧。” 不行——.dict() 已被弃用,实际上不应再用; 请使用 model_dump()。混用 v1/v2 风格是 Pydantic 代码出错的首要原因。
  • 因为“记得”而使用 @root_validator——v2 中已不存在此装饰器;请使用 @model_validator(mode="before"|"after")。
  • 为了验证列表/字典而将基本类型包装进只有一个字段的 RootModel——请改用 TypeAdapter(list[T])。
  • 在这里重新实现 FastAPI 请求/响应处理——那属于 fastapi 技能;本技能负责定义模型,由框架使用这些模型。

输出

正确、最新的 Pydantic v2 源码:使用 ConfigDict 的模型类、带类型和约束的字段、通过 @field_validator/@model_validator 进行验证、使用 model_dump* 序列化、pydantic-settings 配置类,以及 TypeAdapter/判别联合结构——不含任何 v1 时代的惯用写法。使用方是任何需要经过验证、可序列化数据模型的代码或工作流阶段;当这些模型用于 HTTP API 时,fastapi 技能负责框架接线。

相关内容

  • fastapi — 框架细节:请求/响应模型、response_model、通过依赖注入进行验证。此技能定义模型;fastapi 使用这些模型。此处不要重复介绍框架机制。
  • 官方文档(来源):参见 references/sources.md。

渐进式披露

详细内容位于 references/ 中,按需加载:

  • references/validators.md — 编写验证器时加载:所有 @field_validator/@model_validator 模式、ValidationInfo、@classmethod 规则,以及 before/after 顺序。
  • references/serialization.md — 自定义输出时加载:所有 model_dump* 参数、@field_serializer、@model_serializer、@computed_field、别名类型(validation_alias / serialization_alias / AliasChoices)。
  • references/settings.md — 深入了解 pydantic-settings 时加载:嵌套设置、env_nested_delimiter、来源优先级、secrets_dir。
  • references/migration-v1-to-v2.md — 将 v1 代码现代化时加载:完整的重命名表和行为变更。
  • references/sources.md — 研究来源。

正文预算

  • description ≤ 1,024 个字符(符合要求)。
  • 正文约 290 行,远低于 5,000 个 token 的软性目标。
  • 每个参考文件都远低于 10k 个 token 的警告阈值。

评论

支持 Markdown