电话号码的 pydantic 自定义数据类型:value_error.missing

Nav*_*een 4 python sqlalchemy pydantic

用户的 mysql 模式

user_id     binary(16) 
first_name  varchar(32) 
last_name   varchar(32) 
email       varchar(255)
phone       varchar(32) 
role        enum('member','admin','operator')
created_on  datetime
updated_on  datetime
id   varchar(32)
Run Code Online (Sandbox Code Playgroud)

我的 pydantic 模型是

class UserPatchEntity(BaseModel):
    user_id: Optional[UUID]
    first_name: Optional[str] = Field(min_length=1, max_length=32)
    last_name: Optional[str] = Field(min_length=1, max_length=32)
    email: Optional[EmailStr]
    phone: Optional[Phone]---------------> HERE
    role:  Optional[RoleType]
Run Code Online (Sandbox Code Playgroud)

我想为电话号码创建一个自定义数据类型...这样我可以在多个地方使用它...我正在尝试如下

class Phone(BaseModel):
    phone: str
    @validator('phone')
    def phone_validation(cls, v):
        phone = v.phone.get("phone")
        logger.debug(f"phone in 2 validator:{v}")
        regex = r"^(\+)[1-9][0-9\-\(\)\.]{9,15}$"
        if v and not re.search(regex, v, re.I):
            raise ValueError("Phone Number Invalid.")
        return v
    class Config:
        orm_mode = True
        use_enum_values = True
Run Code Online (Sandbox Code Playgroud)

通过邮递员,我正在发送

{
    "phone": "+917777777777"
}
Run Code Online (Sandbox Code Playgroud)

我在邮递员中看到 400 错误

[
    {
        "loc": [
            "phone",
            "phone"
        ],
        "msg": "field required",
        "type": "value_error.missing"
    }
]
Run Code Online (Sandbox Code Playgroud)

关于我上面做错了什么的任何输入

Pau*_*l P 8

这不起作用,因为您正在传递:

{
    "phone": "+917777777777"
}
Run Code Online (Sandbox Code Playgroud)

而模型具有结构UserPatchEntity.Phone.phone

为了使这项工作成功,我看到两个选择:

1. 调整 JSON Body(并修复validator

修复validator

class Phone(BaseModel):
    phone: str
    @validator("phone")
    def phone_validation(cls, v):
        # phone = v.phone.get("phone") #  <-- This line needs to be removed.
        logger.debug(f"phone in 2 validator:{v}")
        regex = r"^(\+)[1-9][0-9\-\(\)\.]{9,15}$"
        if v and not re.search(regex, v, re.I):
            raise ValueError("Phone Number Invalid.")
        return v

    class Config:
        orm_mode = True
        use_enum_values = True
Run Code Online (Sandbox Code Playgroud)

改为发送此内容:

{
  "phone": {
    "phone": "+917777777777"
  }
}
Run Code Online (Sandbox Code Playgroud)

现在应该可以了。

2. 将 移动validator到父模型

我认为以下内容更符合您的要求:

class UserPatchEntity(BaseModel):
    user_id: Optional[UUID]
    first_name: Optional[str] = Field(min_length=1, max_length=32)
    last_name: Optional[str] = Field(min_length=1, max_length=32)
    email: Optional[EmailStr]
    phone: Optional[str]  # <-- This is a str now, everything else stays the same
    role:  Optional[RoleType]

    @validator("phone")
    def phone_validation(cls, v):
        logger.debug(f"phone in 2 validator:{v}")
        regex = r"^(\+)[1-9][0-9\-\(\)\.]{9,15}$"
        if v and not re.search(regex, v, re.I):
            raise ValueError("Phone Number Invalid.")
        return v

    class Config:
        orm_mode = True
        use_enum_values = True
Run Code Online (Sandbox Code Playgroud)

这样你就可以phone按照你所期望的那样传入正文:

{
    "phone": "+917777777777"
}
Run Code Online (Sandbox Code Playgroud)

假设您使用 FastAPI,在这两种情况下,如果电话号码太短、太长或包含无效字符,您会收到以下错误:

{
  "detail": [
    {
      "loc": [
        "body",
        "phone"
      ],
      "msg": "Phone Number Invalid.",
      "type": "value_error"
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

注意/提示:

  • Pydantic 有一个constr(约束字符串)类型,它允许您定义最小和最大长度以及其他一些东西。您可以使用以下内容代替strphone字段,例如:

    class UserPatchEntity(BaseModel):
        ...
        phone: Optional[
            constr(
                strip_whitespace=True,
                min_length=9,
                max_length=15,
            )
        ]
    
    Run Code Online (Sandbox Code Playgroud)

    它还允许您直接指定一个regex字符串,例如:

    class UserPatchEntity(BaseModel):
        ...
        phone: Optional[
            constr(
                strip_whitespace=True,
                regex=r"^(\+)[1-9][0-9\-\(\)\.]{9,15}$",
            )
        ]
    
    Run Code Online (Sandbox Code Playgroud)

    这将节省您编写显式的validator.

  • 如果您继续“直接”使用正则表达式,我建议compile()他们让它们更快一点。

  • http://localhost:8000/docs假设您使用的是 FastAPI,当应用程序运行时,您可以导航到,您将在其中找到完整的 OpenAPI 界面。您可以直接从那里发送有效负载,它还会显示每个端点的签名。在第一种情况下,如果您定义了这样的端点:

    @router.post("/user/")
    async def post_user(user_patch_entity: UserPatchEntity):
        return user_patch_entity.phone
    
    Run Code Online (Sandbox Code Playgroud)

    它会显示这样的文档: 在此输入图像描述