Pydantic constr vs Field args



我想知道两者之间有什么区别:

from pydantic import BaseModel, Field
class Person(BaseModel):
name: str = Field(..., min_length=1)

和:

from pydantic import BaseModel, constr
class Person(BaseModel):
name: constr(min_length=1)

两者似乎执行相同的验证(当name为空字符串时,甚至引发完全相同的异常信息)。这只是代码风格的问题吗?其中一个比另一个更受欢迎吗?

另外,如果我想包含非空字符串列表作为属性,您认为以下哪种方法会更好?

from typing import List
from pydantic import BaseModel, constr
class Person(BaseModel):
languages: List[constr(min_length=1)]

或:

from typing import List    
from pydantic import BaseModel, Field
class Person(BaseModel):
languages: List[str]

@validator('languages', each_item=True)
def check_nonempty_strings(cls, v):
if not v:
raise ValueError('Empty string is not a valid language.')
return v

编辑: FWIW,我正在将其用于 FastAPI 应用程序。

编辑2: 对于我的第二个问题,我认为第一种选择更好,因为它在架构中包含长度要求(因此在文档中)

constr 和 Fields 没有相同的用途。

constr 是一种特定类型,它提供有关此特定类型的验证规则。您拥有所有经典 python 类型的等效项。

Constr的论点:

strip_whitespace: bool = False: removes leading and trailing whitespace
to_lower: bool = False: turns all characters to lowercase
to_upper: bool = False: turns all characters to uppercase
strict: bool = False: controls type coercion
min_length: int = None: minimum length of the string
max_length: int = None: maximum length of the string
curtail_length: int = None: shrinks the string length to the set value when it is longer than the set value
regex: str = None: regex to validate the string against

如您所见,这些参数允许您操作 str 本身,而不是使用此字段的 pydantic 行为。

字段的作用不同,它是一种自定义字段的方法,所有字段不仅是str,它还添加了18个自定义变量,您可以在此处找到。

这只是代码风格的问题吗?其中一个比另一个更受欢迎吗?

对于 STR 的特定情况,这是一个代码风格问题,首选什么并不重要,只有您的用例才重要。

一般来说,最好不要将不同的语法混合在一起,因为您经常需要Field(),您会经常找到它。

一个经典的用例是在驼峰案例或 PascalCase 中发送 json 对象的 api 响应,您将使用字段别名来匹配这些对象并在snake_case中使用它们的变量。

示例:

class Voice(BaseModel):
name: str = Field(None, alias='ActorName')
language_code: str = None
mood: str = None

我个人更喜欢使用 pydantic 类型来清楚地区分类型规则和字段注释。

基本示例:

class Car(BaseModel):
description: Union[constr(min_length=1, max_length=64), None] = Field(
default=None,
example="something",
description="Your car description",
)

在任何情况下,您都应该只使用一种样式的模型结构(字段,pydantic类型或两者兼而有之),以实现项目的全局一致性和更好的可读性。

对于您的第二个问题,您是对的,使用 constr 肯定是最好的方法,因为验证规则将被添加到 OpenAPI 文档中。

如果您想了解有关限制和字段规则实施的更多信息,请查看此内容。

此链接显示了同时适用于pydantic和mypy的方法: https://lyz-code.github.io/blue-book/coding/python/pydantic_types/#using-constrained-strings-in-list-attributes

我的用例的最佳选择是创建一个继承自pydantic.ConstrainedStr的类,如下所示:

import pydantic
from typing import List
...
class Regex(pydantic.ConstrainedStr):
regex = re.compile("^[0-9a-z_]*$")
class Data(pydantic.BaseModel):
regex: List[Regex]
# regex: list[Regex] if you are on 3.9+

我在python 3.9中使用pydantic 1.10.4,这就是我观察到使用constrField之间的区别。

from pydantic import BaseModel, Field, FilePath, constr
from typing import Union, Annotated
ContactConstr = constr(regex='d{3}-d{3}-d{4}')
ContactField = Annotated[str, Field(regex='d{3}-d{3}-d{4}')]
class Person(BaseModel):
contact_with_constr: ContactConstr
contact_with_field: ContactField
contacts_with_constr: Union[ContactConstr, list[ContactConstr]]
contacts_with_field: Union[ContactField, list[ContactField]] # yields incorrect schema
print(Person.schema_json(indent=2))

这会为contacts_with_field创建不正确的模式,该模式采用单个电话号码或xxx-xxx-xxxx形式的电话号码列表,其中x为 0-9。

{
"title": "Person",
"type": "object",
"properties": {
"contact_with_constr": {
"title": "Contact With Constr",
"pattern": "\d{3}-\d{3}-\d{4}",
"type": "string"
},
"contact_with_field": {
"title": "Contact With Field",
"pattern": "\d{3}-\d{3}-\d{4}",
"type": "string"
},
"contacts_with_constr": {
"title": "Contacts With Constr",
"anyOf": [
{
"type": "string",
"pattern": "\d{3}-\d{3}-\d{4}"
},
{
"type": "array",
"items": {
"type": "string",
"pattern": "\d{3}-\d{3}-\d{4}"
}
}
]
},
"contacts_with_field": {
"title": "Contacts With Field",
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"required": [
"contact_with_constr",
"contact_with_field",
"contacts_with_constr",
"contacts_with_field"
]
}

相关内容

  • 没有找到相关文章

最新更新