Python pydantic 验证日期

6

我的问题很简单,我很惊讶还没有人问过:

如何在pydantic中验证日期?

例如,我只想接受1980.1.1-2000.1.1范围内的日期。

2个回答

8

datetime字段的验证器正是您所需要的。您可以按照以下方式使用它:

from pydantic import BaseModel, validator
from datetime import datetime

class DModel(BaseModel):
    dt: datetime

    @validator("dt")
    def ensure_date_range(cls, v):
        if not datetime(year=1980, month=1, day=1) <= v < datetime(year=2000, month=1, day=1):
            raise ValueError("Must be in range")
        return v

DModel.parse_obj({"dt": "1995-01-01T00:00"})
DModel.parse_obj({"dt": "2001-01-01T00:00"})  # validation error

1
我可以为多个pydantic类/模式/模型使用相同的验证器吗? - salius
1
是的,如此处所述。 - alex_noname
链接似乎有问题 @alex_noname - undefined

6

一个常见的用例,可能是根据OP在复数中使用“日期”提示的,是在相同模型中验证多个日期。这个文档记录得很少,而且如果要解析其他日期格式的字符串,还有其他问题需要处理。

这不是在文档中明确说明的。好吧,它在那里,但需要时所述的情况并未提到。如果您需要验证器将非ISO str格式转换为date,Pydantic会在您的验证器运行之前发现无效的日期格式。在此情况下,仅应用多个验证器或将一个验证器应用于多个字段是不足够的,还需要提供pre=True参数,以预先防止默认格式引发异常,然后才能进行验证:

from datetime import datetime, date
from pydantic import BaseModel, validator


def validate_date(value: str) -> date:
    return datetime.strptime(value, "%d/%m/%Y").date()


class CommonModel(BaseModel):
    created: date
    modified: date
    confirmed: date
    closed: date

    _validate_dates = validator(
        'created', 'modified', 'confirmed', 'closed', pre=True, allow_reuse=True,
    )(validate_date)

data = {
    'created': '25/12/2000',
    'modified': '31/12/2000',
    'confirmed': '01/01/2001',
    'closed': '11/09/2001',
}

try:
    model = CommonModel(**data)
except Exception as e:
    print(e.json())

关于这个问题的文档非常简略,我不得不自己发现这一点,而且我还没有找到其他人指出这一点,所以我认为在这里演示这种代码会很有帮助。


如果模型中有一个非日期字段,这个方法就会出错。你知道怎么处理吗? - undefined
这很有趣,@Gibbs,我之前在很多其他字段中使用了这个。希望你没有把非日期字段放在"_validate_dates"的调用中?我只是在检查这个问题,但我现在已经转向Pydantic 2,所以还需要做一些其他的改变。如果可以的话,我会更新我的答案。 - undefined
为了快速回答你的问题,@Gibbs,使用Pydantic 2.3中的field_validator代替validator,将pre=True改为mode='before',并且去掉allow_reuse参数,应该可以适用于不同类型的其他字段。需要验证的字段的名称需要列出来,所以不要在不需要特定验证器的字段中放入字段名称。再次说明,这个部分的文档记录较少或者没有明确记录,这有点令人沮丧,但通过试错也不难弄清楚。我会更新答案以进行澄清。 - undefined
1
+1 我这边同意。感谢你的努力。这是由于字段名称拼写错误导致的。这个方法有效。由于 pydantic2+ 中没有 MetaModelClass 的可用性,我被迫使用 pydantic < 2。我在这个链接 https://stackoverflow.com/questions/67699451/make-every-field-as-optional-with-pydantic 中看到你在装饰器方法中发表了评论。 - undefined
1
我不知道你的环境 @Gibbs,但理论上,Pydantic 2 应该能使一切变得更好、更容易,如果你的环境支持的话。Pydantic 2 的一个小问题是,很多 Stack Overflow 的解决方案都是针对 Pydantic 1 的,但我猜应该可以迁移过来。如果你想让所有字段都是可选的,有一些有用的答案可以参考,而且还有我的另一个问题也涉及到这个需求。 - undefined
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接