字段
API 文档
在本节中,我们将介绍自定义 Pydantic 模型字段的可用机制:默认值、JSON Schema 元数据、约束等。
为此,大量使用了 Field()
函数,其行为方式与标准库 field()
函数对于 dataclasses 相同
from pydantic import BaseModel, Field
class Model(BaseModel):
name: str = Field(frozen=True)
注意
即使 name
被赋值,它仍然是必需的,并且没有默认值。如果您想强调必须提供值,可以使用 省略号
class Model(BaseModel):
name: str = Field(..., frozen=True)
但是,不鼓励使用它,因为它与静态类型检查器配合不佳。
注解模式¶
为了应用约束或将 Field()
函数附加到模型字段,Pydantic 支持 Annotated
类型构造来将元数据附加到注解
from typing import Annotated
from pydantic import BaseModel, Field, WithJsonSchema
class Model(BaseModel):
name: Annotated[str, Field(strict=True), WithJsonSchema({'extra': 'data'})]
就静态类型检查器而言,name
仍然被类型化为 str
,但 Pydantic 利用可用的元数据来添加验证逻辑、类型约束等。
使用这种模式有一些优点
- 使用
f: <type> = Field(...)
形式可能会令人困惑,并可能误导用户认为f
具有默认值,而实际上它仍然是必需的。 - 您可以为字段提供任意数量的元数据元素。如上面的示例所示,
Field()
函数仅支持有限的约束/元数据集,您可能必须在某些情况下使用不同的 Pydantic 实用程序,例如WithJsonSchema
。 - 类型可以是可重用的(请参阅关于 使用注解模式的自定义类型 的文档)。
但是,请注意,Field()
函数的某些参数(即 default
、default_factory
和 alias
)被静态类型检查器考虑在内,以合成正确的 __init__
方法。注解模式 不 被它们理解,因此您应该使用正常的赋值形式代替。
提示
注解模式也可以用于向类型的特定部分添加元数据。例如,可以通过这种方式添加 验证约束
from typing import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
int_list: list[Annotated[int, Field(gt=0)]]
# Valid: [1, 3]
# Invalid: [-1, 2]
默认值¶
可以使用正常的赋值语法或通过向 default
参数提供值来为字段提供默认值
from pydantic import BaseModel, Field
class User(BaseModel):
# Both fields aren't required:
name: str = 'John Doe'
age: int = Field(default=20)
警告
在 Pydantic V1 中,类型注解为 Any
或由 Optional
包装的类型将被赋予 None
的隐式默认值,即使没有显式指定默认值。这在 Pydantic V2 中不再是这种情况。
您还可以将可调用对象传递给 default_factory
参数,该参数将被调用以生成默认值
from uuid import uuid4
from pydantic import BaseModel, Field
class User(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
默认工厂还可以接受单个必需参数,在这种情况下,已验证的数据将作为字典传递。
from pydantic import BaseModel, EmailStr, Field
class User(BaseModel):
email: EmailStr
username: str = Field(default_factory=lambda data: data['email'])
user = User(email='[email protected]')
print(user.username)
#> [email protected]
data
参数将仅包含已验证的数据,基于 模型字段的顺序(如果 username
在 email
之前定义,则上面的示例将失败)。
验证默认值¶
默认情况下,Pydantic 将不验证默认值。可以使用 validate_default
字段参数(或 validate_default
配置值)来启用此行为
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
age: int = Field(default='twelve', validate_default=True)
try:
user = User()
except ValidationError as e:
print(e)
"""
1 validation error for User
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twelve', input_type=str]
"""
可变默认值¶
Python 中一个常见的错误来源是将可变对象用作函数或方法参数的默认值,因为同一个实例最终会在每次调用中重复使用。
dataclasses
模块实际上在这种情况下会引发错误,表明您应该使用 默认工厂 代替。
虽然在 Pydantic 中可以做同样的事情,但这不是必需的。如果默认值不可哈希,Pydantic 将在创建模型的每个实例时创建默认值的深拷贝
from pydantic import BaseModel
class Model(BaseModel):
item_counts: list[dict[str, int]] = [{}]
m1 = Model()
m1.item_counts[0]['a'] = 1
print(m1.item_counts)
#> [{'a': 1}]
m2 = Model()
print(m2.item_counts)
#> [{}]
字段别名¶
提示
阅读 专用章节 中关于别名的更多信息。
对于验证和序列化,您可以为字段定义别名。
定义别名有三种方法
Field(alias='foo')
Field(validation_alias='foo')
Field(serialization_alias='foo')
alias
参数用于验证和序列化。如果您想为验证和序列化分别使用不同的别名,可以使用 validation_alias
和 serialization_alias
参数,它们将仅应用于各自的用例。
这是一个使用 alias
参数的示例
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(alias='username')
user = User(username='johndoe') # (1)!
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True)) # (2)!
#> {'username': 'johndoe'}
- 别名
'username'
用于实例创建和验证。 -
我们正在使用
model_dump()
将模型转换为可序列化格式。请注意,
by_alias
关键字参数默认为False
,必须显式指定才能使用字段(序列化)别名转储模型。您还可以使用
ConfigDict.serialize_by_alias
在模型级别配置此行为。当
by_alias=True
时,序列化期间使用别名'username'
。
如果您只想将别名用于仅验证,可以使用 validation_alias
参数
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(validation_alias='username')
user = User(username='johndoe') # (1)!
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True)) # (2)!
#> {'name': 'johndoe'}
- 验证别名
'username'
在验证期间使用。 - 字段名称
'name'
在序列化期间使用。
如果您只想为序列化定义别名,可以使用 serialization_alias
参数
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(serialization_alias='username')
user = User(name='johndoe') # (1)!
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True)) # (2)!
#> {'username': 'johndoe'}
- 字段名称
'name'
用于验证。 - 序列化别名
'username'
用于序列化。
别名优先级
如果您同时使用 alias
和 validation_alias
或 serialization_alias
,则 validation_alias
将优先于 alias
进行验证,并且 serialization_alias
将优先于 alias
进行序列化。
如果您为 alias_generator
模型设置提供值,则可以通过 alias_priority
字段参数控制字段别名和生成的别名的优先级顺序。您可以在 此处 阅读有关别名优先级的更多信息。
静态类型检查/IDE 支持
如果您为 alias
字段参数提供值,静态类型检查器将使用此别名而不是实际字段名称来合成 __init__
方法
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(alias='username')
user = User(username='johndoe') # (1)!
- 被类型检查器接受。
这意味着当使用 validate_by_name
模型设置(允许在模型验证期间使用字段名称和别名)时,当使用实际字段名称时,类型检查器将报错
from pydantic import BaseModel, ConfigDict, Field
class User(BaseModel):
model_config = ConfigDict(validate_by_name=True)
name: str = Field(alias='username')
user = User(name='johndoe') # (1)!
- 不被类型检查器接受。
如果您仍然希望类型检查器使用字段名称而不是别名,则可以使用 注解模式(仅 Pydantic 理解)
from typing import Annotated
from pydantic import BaseModel, ConfigDict, Field
class User(BaseModel):
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
name: Annotated[str, Field(alias='username')]
user = User(name='johndoe') # (1)!
user = User(username='johndoe') # (2)!
- 被类型检查器接受。
- 不被类型检查器接受。
验证别名
即使 Pydantic 在创建模型实例时将 alias
和 validation_alias
视为相同,但类型检查器仅理解 alias
字段参数。作为一种解决方法,您可以改为同时指定 alias
和 serialization_alias (与字段名称相同),因为
serialization_alias将在序列化期间覆盖
alias`
from pydantic import BaseModel, Field
class MyModel(BaseModel):
my_field: int = Field(validation_alias='myValidationAlias')
与
from pydantic import BaseModel, Field
class MyModel(BaseModel):
my_field: int = Field(
alias='myValidationAlias',
serialization_alias='my_field',
)
m = MyModel(myValidationAlias=1)
print(m.model_dump(by_alias=True))
#> {'my_field': 1}
数值约束¶
有一些关键字参数可以用于约束数值
gt
- 大于lt
- 小于ge
- 大于或等于le
- 小于或等于multiple_of
- 给定数字的倍数allow_inf_nan
- 允许'inf'
、'-inf'
、'nan'
值
这是一个例子
from pydantic import BaseModel, Field
class Foo(BaseModel):
positive: int = Field(gt=0)
non_negative: int = Field(ge=0)
negative: int = Field(lt=0)
non_positive: int = Field(le=0)
even: int = Field(multiple_of=2)
love_for_pydantic: float = Field(allow_inf_nan=True)
foo = Foo(
positive=1,
non_negative=0,
negative=-1,
non_positive=0,
even=2,
love_for_pydantic=float('inf'),
)
print(foo)
"""
positive=1 non_negative=0 negative=-1 non_positive=0 even=2 love_for_pydantic=inf
"""
JSON Schema
在生成的 JSON schema 中
gt
和lt
约束将被转换为exclusiveMinimum
和exclusiveMaximum
。ge
和le
约束将被转换为minimum
和maximum
。multiple_of
约束将被转换为multipleOf
。
上面的代码片段将生成以下 JSON Schema
{
"title": "Foo",
"type": "object",
"properties": {
"positive": {
"title": "Positive",
"type": "integer",
"exclusiveMinimum": 0
},
"non_negative": {
"title": "Non Negative",
"type": "integer",
"minimum": 0
},
"negative": {
"title": "Negative",
"type": "integer",
"exclusiveMaximum": 0
},
"non_positive": {
"title": "Non Positive",
"type": "integer",
"maximum": 0
},
"even": {
"title": "Even",
"type": "integer",
"multipleOf": 2
},
"love_for_pydantic": {
"title": "Love For Pydantic",
"type": "number"
}
},
"required": [
"positive",
"non_negative",
"negative",
"non_positive",
"even",
"love_for_pydantic"
]
}
有关更多详细信息,请参阅 JSON Schema Draft 2020-12。
复合类型上的约束
如果您将字段约束与复合类型一起使用,则在某些情况下可能会发生错误。为了避免潜在的问题,您可以使用 Annotated
from typing import Annotated, Optional
from pydantic import BaseModel, Field
class Foo(BaseModel):
positive: Optional[Annotated[int, Field(gt=0)]]
# Can error in some cases, not recommended:
non_negative: Optional[int] = Field(ge=0)
字符串约束¶
有一些字段可以用来约束字符串
min_length
:字符串的最小长度。max_length
:字符串的最大长度。pattern
:字符串必须匹配的正则表达式。
这是一个例子
from pydantic import BaseModel, Field
class Foo(BaseModel):
short: str = Field(min_length=3)
long: str = Field(max_length=10)
regex: str = Field(pattern=r'^\d*$') # (1)!
foo = Foo(short='foo', long='foobarbaz', regex='123')
print(foo)
#> short='foo' long='foobarbaz' regex='123'
- 只允许数字。
JSON Schema
在生成的 JSON schema 中
min_length
约束将被转换为minLength
。max_length
约束将被转换为maxLength
。pattern
约束将被转换为pattern
。
上面的代码片段将生成以下 JSON Schema
{
"title": "Foo",
"type": "object",
"properties": {
"short": {
"title": "Short",
"type": "string",
"minLength": 3
},
"long": {
"title": "Long",
"type": "string",
"maxLength": 10
},
"regex": {
"title": "Regex",
"type": "string",
"pattern": "^\\d*$"
}
},
"required": [
"short",
"long",
"regex"
]
}
Decimal 约束¶
有一些字段可以用来约束 decimals
max_digits
:Decimal
中数字的最大位数。它不包括小数点前的零或尾随小数零。decimal_places
:允许的最大小数位数。它不包括尾随小数零。
这是一个例子
from decimal import Decimal
from pydantic import BaseModel, Field
class Foo(BaseModel):
precise: Decimal = Field(max_digits=5, decimal_places=2)
foo = Foo(precise=Decimal('123.45'))
print(foo)
#> precise=Decimal('123.45')
Dataclass 约束¶
有一些字段可以用来约束 dataclasses
init
:字段是否应包含在 dataclass 的__init__
中。init_var
:字段是否应被视为 dataclass 中的 仅初始化字段。kw_only
:字段是否应为 dataclass 构造函数中的仅关键字参数。
这是一个例子
from pydantic import BaseModel, Field
from pydantic.dataclasses import dataclass
@dataclass
class Foo:
bar: str
baz: str = Field(init_var=True)
qux: str = Field(kw_only=True)
class Model(BaseModel):
foo: Foo
model = Model(foo=Foo('bar', baz='baz', qux='qux'))
print(model.model_dump()) # (1)!
#> {'foo': {'bar': 'bar', 'qux': 'qux'}}
baz
字段未包含在model_dump()
输出中,因为它是一个仅初始化字段。
字段表示¶
参数 repr
可用于控制字段是否应包含在模型的字符串表示中。
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(repr=True) # (1)!
age: int = Field(repr=False)
user = User(name='John', age=42)
print(user)
#> name='John'
- 这是默认值。
鉴别器¶
参数 discriminator
可用于控制将用于区分联合中不同模型的字段。它接受字段名称或 Discriminator
实例。当鉴别器字段对于 Union
中的所有模型不相同时,Discriminator
方法可能很有用。
以下示例显示了如何将 discriminator
与字段名称一起使用
from typing import Literal, Union
from pydantic import BaseModel, Field
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_type: Literal['dog']
age: int
class Model(BaseModel):
pet: Union[Cat, Dog] = Field(discriminator='pet_type')
print(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})) # (1)!
#> pet=Cat(pet_type='cat', age=12)
from typing import Literal
from pydantic import BaseModel, Field
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_type: Literal['dog']
age: int
class Model(BaseModel):
pet: Cat | Dog = Field(discriminator='pet_type')
print(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})) # (1)!
#> pet=Cat(pet_type='cat', age=12)
以下示例显示了如何将 discriminator
关键字参数与 Discriminator
实例一起使用
from typing import Annotated, Literal, Union
from pydantic import BaseModel, Discriminator, Field, Tag
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_kind: Literal['dog']
age: int
def pet_discriminator(v):
if isinstance(v, dict):
return v.get('pet_type', v.get('pet_kind'))
return getattr(v, 'pet_type', getattr(v, 'pet_kind', None))
class Model(BaseModel):
pet: Union[Annotated[Cat, Tag('cat')], Annotated[Dog, Tag('dog')]] = Field(
discriminator=Discriminator(pet_discriminator)
)
print(repr(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})))
#> Model(pet=Cat(pet_type='cat', age=12))
print(repr(Model.model_validate({'pet': {'pet_kind': 'dog', 'age': 12}})))
#> Model(pet=Dog(pet_kind='dog', age=12))
from typing import Annotated, Literal
from pydantic import BaseModel, Discriminator, Field, Tag
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_kind: Literal['dog']
age: int
def pet_discriminator(v):
if isinstance(v, dict):
return v.get('pet_type', v.get('pet_kind'))
return getattr(v, 'pet_type', getattr(v, 'pet_kind', None))
class Model(BaseModel):
pet: Annotated[Cat, Tag('cat')] | Annotated[Dog, Tag('dog')] = Field(
discriminator=Discriminator(pet_discriminator)
)
print(repr(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})))
#> Model(pet=Cat(pet_type='cat', age=12))
print(repr(Model.model_validate({'pet': {'pet_kind': 'dog', 'age': 12}})))
#> Model(pet=Dog(pet_kind='dog', age=12))
您还可以利用 Annotated
来定义您的可区分联合。有关更多详细信息,请参阅 可区分联合 文档。
严格模式¶
Field
上的 strict
参数指定字段是否应在“严格模式”下验证。在严格模式下,Pydantic 在验证期间抛出错误,而不是在 strict=True
的字段上强制转换数据。
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(strict=True)
age: int = Field(strict=False) # (1)!
user = User(name='John', age='42') # (2)!
print(user)
#> name='John' age=42
- 这是默认值。
age
字段未在严格模式下验证。因此,它可以被赋值为字符串。
有关更多详细信息,请参阅 严格模式。
有关 Pydantic 如何在严格模式和宽松模式下转换数据的更多详细信息,请参阅 转换表。
不可变性¶
参数 frozen
用于模拟冻结 dataclass 行为。它用于防止字段在模型创建后被赋予新值(不可变性)。
有关更多详细信息,请参阅 冻结 dataclass 文档。
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
name: str = Field(frozen=True)
age: int
user = User(name='John', age=42)
try:
user.name = 'Jane' # (1)!
except ValidationError as e:
print(e)
"""
1 validation error for User
name
Field is frozen [type=frozen_field, input_value='Jane', input_type=str]
"""
- 由于
name
字段被冻结,因此不允许赋值。
排除¶
exclude
参数可用于控制在导出模型时应从模型中排除哪些字段。
请参见以下示例
from pydantic import BaseModel, Field
class User(BaseModel):
name: str
age: int = Field(exclude=True)
user = User(name='John', age=42)
print(user.model_dump()) # (1)!
#> {'name': 'John'}
age
字段未包含在model_dump()
输出中,因为它被排除在外。
有关更多详细信息,请参阅 序列化 部分。
已弃用字段¶
deprecated
参数可用于将字段标记为已弃用。这样做将导致
- 在访问字段时发出运行时弃用警告。
- 在生成的 JSON schema 中设置
"deprecated": true
。
您可以将 deprecated
参数设置为以下之一
- 一个字符串,将用作弃用消息。
warnings.deprecated
装饰器(或typing_extensions
向后移植)的实例。- 一个布尔值,将用于将字段标记为已弃用,并带有默认的
'deprecated'
弃用消息。
deprecated
作为字符串¶
from typing import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated='This is deprecated')]
print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
通过 warnings.deprecated
装饰器弃用¶
注意
只有在安装了 typing_extensions
>= 4.9.0 的情况下,才能以这种方式使用 deprecated
装饰器。
import importlib.metadata
from typing import Annotated, deprecated
from packaging.version import Version
from pydantic import BaseModel, Field
if Version(importlib.metadata.version('typing_extensions')) >= Version('4.9'):
class Model(BaseModel):
deprecated_field: Annotated[int, deprecated('This is deprecated')]
# Or explicitly using `Field`:
alt_form: Annotated[
int, Field(deprecated=deprecated('This is deprecated'))
]
deprecated
作为布尔值¶
from typing import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated=True)]
print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
支持 category
和 stacklevel
此功能的当前实现未考虑 deprecated
装饰器的 category
和 stacklevel
参数。这可能会在 Pydantic 的未来版本中实现。
在验证器中访问已弃用字段
在验证器内部访问已弃用字段时,将发出弃用警告。您可以使用 catch_warnings
显式忽略它
import warnings
from typing_extensions import Self
from pydantic import BaseModel, Field, model_validator
class Model(BaseModel):
deprecated_field: int = Field(deprecated='This is deprecated')
@model_validator(mode='after')
def validate_model(self) -> Self:
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
self.deprecated_field = self.deprecated_field * 2
自定义 JSON Schema¶
某些字段参数专门用于自定义生成的 JSON schema。相关的参数是
title
description
examples
json_schema_extra
在 JSON schema 文档的 自定义 JSON Schema 部分中阅读有关使用字段自定义/修改 JSON schema 的更多信息。
computed_field
装饰器¶
API 文档
computed_field
装饰器可用于在序列化模型或 dataclass 时包含 property
或 cached_property
属性。该属性也将在 JSON Schema 中考虑(在序列化模式下)。
注意
属性对于从其他字段计算的字段,或对于计算成本高昂的字段(因此,如果使用 cached_property
则会缓存)很有用。
但是,请注意,Pydantic 将不对包装的属性执行任何额外的逻辑(验证、缓存失效等)。
这是一个为具有计算字段的模型生成的 JSON schema(在序列化模式下)的示例
from pydantic import BaseModel, computed_field
class Box(BaseModel):
width: float
height: float
depth: float
@computed_field
@property # (1)!
def volume(self) -> float:
return self.width * self.height * self.depth
print(Box.model_json_schema(mode='serialization'))
"""
{
'properties': {
'width': {'title': 'Width', 'type': 'number'},
'height': {'title': 'Height', 'type': 'number'},
'depth': {'title': 'Depth', 'type': 'number'},
'volume': {'readOnly': True, 'title': 'Volume', 'type': 'number'},
},
'required': ['width', 'height', 'depth', 'volume'],
'title': 'Box',
'type': 'object',
}
"""
这是一个使用带有计算字段的 model_dump
方法的示例
from pydantic import BaseModel, computed_field
class Box(BaseModel):
width: float
height: float
depth: float
@computed_field
@property # (1)!
def volume(self) -> float:
return self.width * self.height * self.depth
b = Box(width=1, height=2, depth=3)
print(b.model_dump())
#> {'width': 1.0, 'height': 2.0, 'depth': 3.0, 'volume': 6.0}
- 如果未指定,
computed_field
将隐式地将方法转换为property
。但是,出于类型检查的目的,最好显式使用@property
装饰器。
与常规字段一样,计算字段也可以标记为已弃用
from typing_extensions import deprecated
from pydantic import BaseModel, computed_field
class Box(BaseModel):
width: float
height: float
depth: float
@computed_field
@property
@deprecated("'volume' is deprecated")
def volume(self) -> float:
return self.width * self.height * self.depth