我可以从 Pydantic 获取传入的额外字段吗?

Jyo*_*may 28 python python-3.x pydantic

我在 Pydantic Config 中定义了 pydantic 架构extra = Extra.allow

是否可以获取单独传递给架构的一个列表或一组额外字段。

例如:

from pydantic import BaseModel as pydanticBaseModel
class BaseModel(pydanticBaseModel):
    name: str

    class Config:
        allow_population_by_field_name = True
        extra = Extra.allow
Run Code Online (Sandbox Code Playgroud)

我传递以下 JSON:

   {
    "name": "Name",
    "address": "bla bla",
    "post": "post"
   }
Run Code Online (Sandbox Code Playgroud)

我需要一个来自 的函数pydantic(如果可用),它将返回传递的所有额外字段。喜欢:{"address", "post"}

fla*_*kes 33

注意:此答案适用于 Pydantic 1.x 版本。有关 2.x 解决方案,请参阅robcxyz 的答案。


来自pydantic 文档

额外的

在模型初始化期间是否忽略、允许或禁止额外属性。'ignore'接受、'allow'、 或 的字符串值,'forbid'或枚举值Extra(默认值:Extra.ignore)。 'forbid'如果包含额外的属性,将导致验证失败,'ignore'将默默地忽略任何额外的属性,并将 'allow'属性分配给模型。

这可以包含在模型Config类中,也可以作为继承时的参数BaseModel

from pydantic import BaseModel, Extra

class BaseModel(BaseModel, extra=Extra.allow):
    name: str


model = Model.parse_obj(
   {"name": "Name", "address": "bla bla", "post": "post"}
)

print(model)
# name='Name' post='post' address='bla bla'
Run Code Online (Sandbox Code Playgroud)

__fields__要获得额外的值,您可以做一些简单的事情,例如将类上定义的集合与__dict__实例上的值进行比较:

class Model(BaseModel, extra=Extra.allow):
    python_name: str = Field(alias="name")

    @property
    def extra_fields(self) -> set[str]:
        return set(self.__dict__) - set(self.__fields__)
Run Code Online (Sandbox Code Playgroud)
>>> Model.parse_obj({"name": "Name", "address": "bla bla", "post": "post"}).extra_fields
{'address', 'post'}
>>> Model.parse_obj({"name": "Name", "foobar": "fizz"}).extra_fields
{'foobar'}
>>> Model.parse_obj({"name": "Name"}).extra_fields
set()
Run Code Online (Sandbox Code Playgroud)

  • @YaakovBressler“工作”是什么意思以及“递归”对象是什么意思?每个嵌套对象将按照其模型类的定义独立加载其 json 部分。因此,如果您希望它适用于嵌套层次结构,则组合中的每个模型都需要提供相同的技巧。 (2认同)

rob*_*xyz 22

Pydantic 额外字段行为在 2.0 版本中进行了更新。根据他们的文档,您现在不需要执行任何操作,只需将model_config extra字段设置为allow,然后可以使用该model_extra字段或__pydantic_extra__实例属性来获取额外字段的字典。

from pydantic import BaseModel, Field, ConfigDict


class Model(BaseModel):
    python_name: str = Field(alias="name")

    model_config = ConfigDict(
        extra='allow',
    )

m = Model(**{"name": "Name", "address": "bla bla", "post": "post"}).model_extra
assert m == {'address': 'bla bla', 'post': 'post'}

m = Model(**{"name": "Name", "foobar": "fizz"}).__pydantic_extra__
assert m == {'foobar': 'fizz'}

m = Model(**{"name": "Name"}).__pydantic_extra__
assert m == {}
Run Code Online (Sandbox Code Playgroud)


ale*_*ame 18

据我所知,对此没有现成的解决方案。但是,由于您可以访问pre 根验证器中的所有“原始”传递数据,包括额外的字段,因此您可以将它们放在单独的字典中。

一个例子是从这里获取的。感谢@PrettyWood 提供:

from typing import Any, Dict, Optional

from pydantic import BaseModel, Field, root_validator


class ComposeProject(BaseModel):
    version: Optional[str] = Field(alias='version')  # just to show that it supports alias too
    extra: Dict[str, Any]

    @root_validator(pre=True)
    def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        all_required_field_names = {field.alias for field in cls.__fields__.values() if field.alias != 'extra'}  # to support alias

        extra: Dict[str, Any] = {}
        for field_name in list(values):
            if field_name not in all_required_field_names:
                extra[field_name] = values.pop(field_name)
        values['extra'] = extra
        return values


project = ComposeProject(**{
  "version": "3",
  "services": ...
})
print(project.version)  # => '3'
print(project.extra)  # => {'services': Ellipsis}
Run Code Online (Sandbox Code Playgroud)