【问题标题】:Pydantic set attribute/field to model dynamicallyPydantic 设置属性/字段以动态建模
【发布时间】:2022-08-16 19:32:50
【问题描述】:

根据docs

允许突变

模型是否是假不可变的,即是否设置允许(默认值:True)

好吧,我有一堂课:

class MyModel(BaseModel):

    field1:int

    class Config:
        allow_mutation = True

如果我尝试动态添加一个字段:

model1 = MyModel(field1=1)
model1.field2 = 2

我得到这个错误:

  File \"pydantic/main.py\", line 347, in pydantic.main.BaseModel.__setattr__
ValueError: \"MyModel\" object has no field \"field2\"

显然,使用setattr 方法会导致同样的错误。

setattr(model1, \'field2\', 2)

输出:

  File \"pydantic/main.py\", line 347, in pydantic.main.BaseModel.__setattr__
ValueError: \"MyModel\" object has no field \"field2\"

我在这里错过了什么?

  • setattr 呼叫不应该在model1 上而不是field2,即setattr(model1, \'field2\', 2) 上吗?
  • 问题中的错字:已修复!谢谢@JoshClark。问题当然还在这里
  • 是否允许 setattr现存的领域?即field1,而不是field2。尝试将其设置为 false,创建一个实例,然后在该实例上再次设置 field1。那可能是它控制而不是添加随意的属性。
  • @JLPeyret 这是否意味着不可能动态创建新字段?
  • 如果我的建议是正确的,那么是的,这可能意味着。我广泛使用 pydantic,但还不是很复杂。但它似乎不太适合使用动态代码,否则我会做很多事情。 dynvars : dict[str,Any] = field(factory=dict) 不确定工厂语法并使用额外的随机字段进行变异怎么办?

标签: python pydantic


【解决方案1】:

实现此目的的一种“天真”方法是覆盖模型声明中的__setattr__

class MyModel(BaseModel):

    field1:int

    def __setattr__(self, key, value):
        return object.__setattr__(self, key, value)

这是“工作”,但这显然很糟糕,完全绕过了进行 JSON 模式验证的想法(感谢 pydantic)。

因此,我最终决定声明一个可以扩展 MyModel 字段的可能动态字段列表,而不是完全绕过 pydantic 验证。

然后覆盖__setattr__ 方法:

class MyModel(BaseModel):

    field1:int

    @property
    def allowed_dynamic_fields(self):
        return ["field2"]

    def __setattr__(self, key, value):
        if key in self.allowed_dynamic_fields:
            # Bypass Pydantic validation only for certain fields.
            return object.__setattr__(self, key, value)
        # Keep Pydantic validations for declared fields
        return super().__setattr__(key, value)

当然,我们可以通过验证类型等其他内容而不是返回硬编码值的简单列表来增加allowed_dynamic_fields 属性的复杂性,但想法就在这里。

【讨论】:

  • 我猜。恕我直言,仍然不是很有活力。我会让事情变得更通用一点,比如if key in getattr(self,"allowed_dynamic_fields",[])class MyModel:\nallowed_dynamic_fields = ["field2"] rather than a property. The other thing is that you can subclass Pydantic models, so you could just add MyModel as a sublass of an ancestor with only field1, say class MyModelF1only(BaseModel). Then you could use a factory method to decide to build either MyModelF1only` 或MyModel。因为,您仍然必须明确决定构建MyModel
  • @JLPeyret:在这里我返回一个简单的“完整”字符串列表,但实际上,我并不真正知道字段的完整名称,只是一个前缀。 allowed_dynamic_fields 是前缀列表。在__setattr__ 中,我基本上检查的是前缀而不是全名。不确定我是否可以通过继承来实现这一点,因为在子类中,我必须声明整个名称而不仅仅是前缀。
  • 好的,这是有道理的。
【解决方案2】:

如果我没记错的话,你必须添加一行:extra = "allow" 至:类配置:

【讨论】:

    猜你喜欢
    • 2021-10-21
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 2020-10-24
    • 2021-07-28
    相关资源
    最近更新 更多