【问题标题】:Python [Pydantic] - default values based other object in the classPython [Pydantic] - 基于类中其他对象的默认值
【发布时间】:2021-03-03 17:21:28
【问题描述】:

如果未提供电子邮件,我想设置默认电子邮件, 即:

name = a 
last_name = b
email = None 

电子邮件将变为“a_b@email.com”

我尝试了类似的方法,但显然不能作为 name,last_name 不在函数中定义。

class User(BaseModel):
     name: Optional[str] = ''
     last_name: Optional[str] = ''
     email: EmailStr
    

    @validator('email')
    def set_email(cls, email):
        if not email:
           return name + last_name + '@email.com' 
        else:
           return email

更新 - 仍然无法正常工作, 我试过了:

    @root_validator(pre=True)
    def email_set_config(cls, values):
        email, name ,last_name = values.get('email'), values.get('name') , values.get('last_name')
        if email is None :
            email= name + '_' + name + '@' + last_name 
        return values

解决方案:

添加到类:

--更新

  • 实际上它在没有定义类 Config 的情况下工作:
    class Config:
        validate_assignment = True
    @validator('email')
    def set_email(cls, v, values, **kwargs):
        return email or values["name"] + '_' + values["name"] + '@' + ["last_name"]

【问题讨论】:

  • 尝试使用@root_validator
  • @NobbyNobbs - 感谢您的建议,不工作,更新后。
  • 实际上它可以工作,您不会在实现中更新值字典,而是设置局部变量。只需修复它:)
  • @NobbyNobbs - 想通了,我在帖子中更新了解决方案,你能解释一下什么时候需要 root_validator 吗?

标签: python pydantic


【解决方案1】:

我稍微改变了你的模型,现在它有两个必填字段和一个可选字段,这是根据其他两个字段计算得出的。

from typing import Optional

import pydantic


class User(pydantic.BaseModel):
    first_name: str
    last_name: str
    email: Optional[pydantic.EmailStr]

在创建实例期间验证数据并同时拥有完整模型上下文的第一种方法是使用@pydantic.root_validator:

    @pydantic.root_validator
    def email_set_config(cls, values):
        email, name, last_name = values.get('email'), values.get('first_name'), values.get('last_name')
        if email is None:
            values["email"] = name + '_' + last_name + '@email.com'
        return values


if __name__ == "__main__":
    u = User(first_name="jon", last_name="doe")
    print(u)  # first_name='jon' last_name='doe' email='jon_doe@email.com'

但如果你只想控制实例化过程,我建议你像这样在你的模型中覆盖__init__ dunder

    def __init__(__pydantic_self__, **data: Any) -> None:
        email, name, last_name = data.get('email'), data.get('first_name'), data.get('last_name')
        if email is None:
            data["email"] = name + '_' + last_name + '@email.com'
        super().__init__(**data)

在我看来,它比验证器更简单直观,我更喜欢它。

您提到的非根验证器的可能解决方案对我来说具有棘手且有点意外的行为。

如果您将命名的 email 参数传递给构造函数,它会像预期的那样工作,但如果您不这样做,则不会。

    @pydantic.validator('email')
    def set_email(cls, v, values, **kwargs):
        return v or values["first_name"] + '_' + values["last_name"] + '@email.com'


if __name__ == "__main__":
    print(User(first_name="jon", last_name="doe"))  # first_name='jon' last_name='doe' email=None
    print(User(first_name="jon", last_name="doe", email=None))  # first_name='jon' last_name='doe' email='jon_doe@email.com'

【讨论】:

  • NobbyNobbs,非常感谢您的详细解释!学到了一些有用的东西:)
【解决方案2】:

没有足够的声誉可以发表评论,所以我将在这里扩展@NobbyNobbs 的答案^^

对于使用 pydantic.validator 的最后一个示例,即使未提供值,您也可以使用 always kwargs 始终运行验证器。

文档中的链接:https://pydantic-docs.helpmanual.io/usage/validators/#validate-always

所以你的例子是:

@pydantic.validator('email', always=True)
def set_email(cls, v, values, **kwargs):    
    return v or values["first_name"] + '_' + values["last_name"] + '@email.com'


if __name__ == "__main__":
    print(User(first_name="jon", last_name="doe"))  # first_name='jon' last_name='doe' email='jon_doe@email.com'
    print(User(first_name="jon", last_name="doe", email=None))  # first_name='jon' last_name='doe' email='jon_doe@email.com'

【讨论】:

    【解决方案3】:

    在我的情况下,解决方案是:

    @pydantic.root_validator(pre=True)
    def method_before_validation(cls, data):
    

    我在一个复杂对象的根类中使用了它。 我有类似订单的对象和客户的单独对象,我需要将客户的 id 放到每个订单的对象中。 所以我们可以在实际验证之前使用数据结构、类型等进行数据修改。 太棒了!

    感谢这篇文章。

    【讨论】:

      【解决方案4】:

      Pydantic 从 v1.5 开始添加了default_factory,您可以使用下面的代码来获得默认的 EmailStr 值,以及其他子模型类对象

      class User(BaseModel):
           name: Optional[str] = ''
           last_name: Optional[str] = ''
           email: EmailStr = Field(
               default_factory=lambda: EmailStr('foo@example.org'))
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-22
        • 1970-01-01
        • 2020-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多