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

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

How to set pydantic model minimum size

问题

我有一个以下的Pydantic模型:

  1. class model(BaseModel):
  2. name: Optional[str]
  3. age: Optional[int]
  4. gender: Optional[str]

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

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

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

英文:

I have a pydantic model below:

  1. class model(BaseModel):
  2. name: Optional[str]
  3. age: Optional[int]
  4. 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

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

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

答案1

得分: 3

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

  1. 最直接的解决方案是一个 [root 验证器][1] M.O. 在上面的评论中提到的
  2. ```python
  3. from typing import Any, Optional
  4. from pydantic import BaseModel, root_validator
  5. class Model(BaseModel):
  6. foo: Optional[str]
  7. bar: Optional[int]
  8. baz: Optional[float]
  9. @root_validator
  10. def ensure_one_field_is_set(cls, data: dict[str, Any]) -> dict[str, Any]:
  11. if all(value is None for value in data.values()):
  12. raise ValueError("至少必须提供一个值")
  13. return data

演示:

  1. from pydantic import ValidationError
  2. print(Model(bar=69).json(indent=4))
  3. try:
  4. Model()
  5. except ValidationError as err:
  6. print(err.json(indent=4))

输出:

  1. {
  2. "foo": null,
  3. "bar": 69,
  4. "baz": null
  5. }
  1. [
  2. {
  3. "loc": [
  4. "__root__"
  5. ],
  6. "msg": "至少必须提供一个值",
  7. "type": "value_error"
  8. }
  9. ]

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

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

  1. class SubModel(Model):
  2. @root_validator
  3. def set_baz_if_all_are_none(cls, data: dict[str, Any]) -> dict[str, Any]:
  4. if all(value is None for value in data.values()):
  5. data["baz"] = 3.14
  6. return data
  7. SubModel() # 将引发错误,`baz` 不会被设置

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

  1. class SubModel(Model):
  2. @root_validator
  3. def set_baz_if_all_are_none(cls, data: dict[str, Any]) -> dict[str, Any]:
  4. if all(value is None for value in data.values()):
  5. data["baz"] = 3.14
  6. return data
  7. @root_validator
  8. def ensure_one_field_is_set(cls, data: dict[str, Any]) -> dict[str, Any]:
  9. return data # 什么也不做
  10. print(SubModel()) # foo=None bar=None baz=3.14

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

  1. from typing import Any, Optional
  2. from pydantic import BaseModel, ValidationError
  3. from pydantic.error_wrappers import ErrorWrapper
  4. class Model(BaseModel):
  5. foo: Optional[str]
  6. bar: Optional[int]
  7. baz: Optional[float]
  8. def __init__(self, **kwargs: Any) -> None:
  9. super().__init__(**kwargs)
  10. if all(value is None for _field, value in self):
  11. raise ValidationError(
  12. [
  13. ErrorWrapper(
  14. ValueError("至少必须提供一个值"),
  15. "__root__",
  16. )
  17. ],
  18. self.__class__,
  19. )

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

  1. <details>
  2. <summary>英文:</summary>
  3. The most straightforward solution is a [root validator][1] as mentionend by M.O. in the comments above:
  4. ```python
  5. from typing import Any, Optional
  6. from pydantic import BaseModel, root_validator
  7. class Model(BaseModel):
  8. foo: Optional[str]
  9. bar: Optional[int]
  10. baz: Optional[float]
  11. @root_validator
  12. def ensure_one_field_is_set(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
  13. if all(value is None for value in data.values()):
  14. raise ValueError(&quot;At least one value must be provided&quot;)
  15. return data

Demo:

  1. from pydantic import ValidationError
  2. print(Model(bar=69).json(indent=4))
  3. try:
  4. Model()
  5. except ValidationError as err:
  6. print(err.json(indent=4))

Output:

  1. {
  2. &quot;foo&quot;: null,
  3. &quot;bar&quot;: 69,
  4. &quot;baz&quot;: null
  5. }
  1. [
  2. {
  3. &quot;loc&quot;: [
  4. &quot;__root__&quot;
  5. ],
  6. &quot;msg&quot;: &quot;At least one value must be provided&quot;,
  7. &quot;type&quot;: &quot;value_error&quot;
  8. }
  9. ]

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:

  1. class SubModel(Model):
  2. @root_validator
  3. def set_baz_if_all_are_none(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
  4. if all(value is None for value in data.values()):
  5. data[&quot;baz&quot;] = 3.14
  6. return data
  7. 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:

  1. class SubModel(Model):
  2. @root_validator
  3. def set_baz_if_all_are_none(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
  4. if all(value is None for value in data.values()):
  5. data[&quot;baz&quot;] = 3.14
  6. return data
  7. @root_validator
  8. def ensure_one_field_is_set(cls, data: dict[str, Any]) -&gt; dict[str, Any]:
  9. return data # do nothing
  10. 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:

  1. from typing import Any, Optional
  2. from pydantic import BaseModel, ValidationError
  3. from pydantic.error_wrappers import ErrorWrapper
  4. class Model(BaseModel):
  5. foo: Optional[str]
  6. bar: Optional[int]
  7. baz: Optional[float]
  8. def __init__(self, **kwargs: Any) -&gt; None:
  9. super().__init__(**kwargs)
  10. if all(value is None for _field, value in self):
  11. raise ValidationError(
  12. [
  13. ErrorWrapper(
  14. ValueError(&quot;At least one value must be provided&quot;),
  15. &quot;__root__&quot;,
  16. )
  17. ],
  18. self.__class__,
  19. )

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:

确定