如何设置pydantic模型的最小大小

huangapple go评论96阅读模式
英文:

How to set pydantic model minimum size

问题

我有一个以下的Pydantic模型:

class model(BaseModel):
    name: Optional[str] 
    age: Optional[int]
    gender: Optional[str]

并且使用这个模型验证请求体。但是想要将Pydantic模型的最小大小设置为1,以便端点不会处理空输入。

@app.post("my_url")
def test(req: dict=model):
    一些代码

可以在请求体中使用长度函数,但尝试找到一些Pydantic的方法。请帮忙!

英文:

I have a pydantic model below:

class model(BaseModel):
    name: Optional[str] 
    age: Optional[int]
    gender: Optional[str]

and validating the request body using this model. but want to set minimum size of pydantic model to be 1 so endpoint should not process empty input

@app.post("my_url")
def test(req: dict=model):
    some code

can use length function inside body but trying to find some pydantic way.
Please help !

答案1

得分: 3

以下是您要翻译的代码部分:

最直接的解决方案是一个 [root 验证器][1]如 M.O. 在上面的评论中提到的

```python
from typing import Any, Optional

from pydantic import BaseModel, root_validator


class Model(BaseModel):
    foo: Optional[str]
    bar: Optional[int]
    baz: Optional[float]

    @root_validator
    def ensure_one_field_is_set(cls, data: dict[str, Any]) -> dict[str, Any]:
        if all(value is None for value in data.values()):
            raise ValueError("至少必须提供一个值")
        return data

演示:

from pydantic import ValidationError


print(Model(bar=69).json(indent=4))
try:
    Model()
except ValidationError as err:
    print(err.json(indent=4))

输出:

{
    "foo": null,
    "bar": 69,
    "baz": null
}
[
    {
        "loc": [
            "__root__"
        ],
        "msg": "至少必须提供一个值",
        "type": "value_error"
    }
]

然而,这种方法存在一个潜在的陷阱。

根验证器按照它们在模型中注册的顺序被调用。这意味着,如果您子类化 Model 并添加一个新的 root 验证器,该验证器将设置一个字段为非-None 值,那么之前的根验证器仍然会首先被调用,新的验证器在介入之前将引发错误:

class SubModel(Model):
    @root_validator
    def set_baz_if_all_are_none(cls, data: dict[str, Any]) -> dict[str, Any]:
        if all(value is None for value in data.values()):
            data["baz"] = 3.14
        return data

SubModel()  # 将引发错误,`baz` 不会被设置

当然,这是一个假设的示例,但仍然值得注意。您可以通过明确 覆盖 先前的验证器方法来解决此问题:

class SubModel(Model):
    @root_validator
    def set_baz_if_all_are_none(cls, data: dict[str, Any]) -> dict[str, Any]:
        if all(value is None for value in data.values()):
            data["baz"] = 3.14
        return data

    @root_validator
    def ensure_one_field_is_set(cls, data: dict[str, Any]) -> dict[str, Any]:
        return data  # 什么也不做


print(SubModel())  # foo=None bar=None baz=3.14

另一种方法是在自己的 __init__ 方法中执行检查并手动构建一个 ValidationError

from typing import Any, Optional

from pydantic import BaseModel, ValidationError
from pydantic.error_wrappers import ErrorWrapper


class Model(BaseModel):
    foo: Optional[str]
    bar: Optional[int]
    baz: Optional[float]

    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)
        if all(value is None for _field, value in self):
            raise ValidationError(
                [
                    ErrorWrapper(
                        ValueError("至少必须提供一个值"),
                        "__root__",
                    )
                ],
                self.__class__,
            )

现在,使用 Model() 将产生与上面根验证器相同的错误。


<details>
<summary>英文:</summary>

The most straightforward solution is a [root validator][1] as mentionend by M.O. in the comments above:

```python
from typing import Any, Optional

from pydantic import BaseModel, root_validator


class Model(BaseModel):
    foo: Optional[str]
    bar: Optional[int]
    baz: Optional[float]

    @root_validator
    def ensure_one_field_is_set(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
        if all(value is None for value in data.values()):
            raise ValueError(&quot;At least one value must be provided&quot;)
        return data

Demo:

from pydantic import ValidationError


print(Model(bar=69).json(indent=4))
try:
    Model()
except ValidationError as err:
    print(err.json(indent=4))

Output:

{
    &quot;foo&quot;: null,
    &quot;bar&quot;: 69,
    &quot;baz&quot;: null
}
[
    {
        &quot;loc&quot;: [
            &quot;__root__&quot;
        ],
        &quot;msg&quot;: &quot;At least one value must be provided&quot;,
        &quot;type&quot;: &quot;value_error&quot;
    }
]

However there is one potential pitfall with this approach.

Root validators are called in the order they were registered with model. This means, if you subclass Model and add a new root validator that sets one of the fields to not-None, the previous root validator will still be called first and it will raise an error before the new validator gets a chance to intervene:

class SubModel(Model):
    @root_validator
    def set_baz_if_all_are_none(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
        if all(value is None for value in data.values()):
            data[&quot;baz&quot;] = 3.14
        return data

SubModel()  # will cause an error, `baz` will not be set

Granted, this is a contrived example, but still something to keep in mind. You can work around this by explicitly overriding the previous validator method:

class SubModel(Model):
    @root_validator
    def set_baz_if_all_are_none(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
        if all(value is None for value in data.values()):
            data[&quot;baz&quot;] = 3.14
        return data

    @root_validator
    def ensure_one_field_is_set(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
        return data  # do nothing


print(SubModel())  # foo=None bar=None baz=3.14

An alternative would be to simply do the check in your own __init__ method and manually construct a ValidationError:

from typing import Any, Optional

from pydantic import BaseModel, ValidationError
from pydantic.error_wrappers import ErrorWrapper


class Model(BaseModel):
    foo: Optional[str]
    bar: Optional[int]
    baz: Optional[float]

    def __init__(self, **kwargs: Any) -&gt; None:
        super().__init__(**kwargs)
        if all(value is None for _field, value in self):
            raise ValidationError(
                [
                    ErrorWrapper(
                        ValueError(&quot;At least one value must be provided&quot;),
                        &quot;__root__&quot;,
                    )
                ],
                self.__class__,
            )

Doing Model() would now produce the same error as with the root validator above.

huangapple
  • 本文由 发表于 2023年5月17日 18:10:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76270941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定