将日期时间表示为日期和时间的 Pydantic 模型

问题描述 投票:0回答:1

我为日期时间创建了一个 Pydantic 模型,它将处理将看起来像

{ "date": "2021-07-01", "time": "12:36:23" }
的 JSON 对象解析为
datetime(2021, 7, 1, 12, 36, 23)
。它还为模型生成正确的 JSON 架构。

class TimestampWithSplit(RootModel):
    root: datetime

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        return core_schema.chain_schema(
            [
                core_schema.typed_dict_schema(
                    {
                        "date": core_schema.typed_dict_field(core_schema.date_schema()),
                        "time": core_schema.typed_dict_field(core_schema.time_schema()),
                    }
                ),
                core_schema.no_info_plain_validator_function(cls.validate_to_datetime),
            ]
        )

    @staticmethod
    def validate_to_datetime(value: dict) -> datetime:
        return datetime.combine(value["date"], value["time"])

我现在正在尝试做两件事:

  1. 我现在想向生成的 json 模式添加描述。目前
    TimestampWithSplit.model_json_schema()
    回归
{'properties': {'date': {'format': 'date', 'title': 'Date', 'type': 'string'},
                'time': {'format': 'time', 'title': 'Time', 'type': 'string'}},
 'required': ['date', 'time'],
 'type': 'object'}

我想补充一下

{'properties': {'date': {'format': 'date', 'title': 'Date', 'type': 'string', 'description': 'ISO format date, blah blah'},
                'time': {'format': 'time', 'title': 'Time', 'type': 'string', 'description': 'ISO format time, blah blah'}},
 'required': ['date', 'time'],
 'type': 'object'}
  1. 为日期字段添加自定义验证器,以便它可以解析个位数的日期和月份数字。我通常会这样做:
    def validator(value: str):
        try:
            return datetime.strptime(value, "%Y-%m-%d").date()
        except ValueError:
            return None

并将该验证器添加为字段上的普通验证器。但我不确定如何将其合并到我所拥有的内容中。

我走错路了吗?如何拥有一个模型,该模型将日期和时间作为单独的字段,但从

datetime
返回
model_validate_json
,同时还自定义 JSON 架构和日期验证?

python pydantic
1个回答
0
投票

我可能不清楚你的目标,所以如果这不适用,我很抱歉,但看起来你最好使用带有

BaseModel
date
属性的
time
而不是使用你的
RootModel
解决方案。给定这样的代码:

import datetime
from pydantic import BaseModel, Field, computed_field, field_validator


class TimestampWithSplit(BaseModel):
    date: datetime.date = Field(
        ..., description="ISO format date", repr=False, exclude=True
    )
    time: datetime.time = Field(
        ..., description="ISO format time", repr=False, exclude=True
    )

    @computed_field
    @property
    def timestamp(self) -> datetime.datetime:
        return datetime.datetime.combine(self.date, self.time)

我们可以从字典中初始化它:

>>> t=TimestampWithSplit.model_validate({'date': '2024-09-24', 'time': '11:00:00'})
>>> t
TimestampWithSplit(timestamp=datetime.datetime(2024, 9, 24, 11, 0))

我们可以使用关键字参数来初始化它:

>>> t=TimestampWithSplit(date='2024-09-24', time='11:00:00')
>>> t
TimestampWithSplit(timestamp=datetime.datetime(2024, 9, 24, 11, 0))

如果您想支持单位数月/日,例如

2024-9-1
,您可以为
date
字段添加字段验证器。也许是这样的:

@field_validator("date", mode="before")
@classmethod
def validate_date(cls, v: str | datetime.date) -> datetime.date:
    if isinstance(v, datetime.date):
        return v
    elif isinstance(v, str):
        return datetime.datetime.strptime(v, "%Y-%m-%d").date()
    else:
        raise ValueError("invalid date")

这将为您提供所需的架构:

>>> print(json.dumps(t.schema(), indent=2))
{
  "properties": {
    "date": {
      "description": "ISO format date",
      "format": "date",
      "title": "Date",
      "type": "string"
    },
    "time": {
      "description": "ISO format time",
      "format": "time",
      "title": "Time",
      "type": "string"
    }
  },
  "required": [
    "date",
    "time"
  ],
  "title": "TimestampWithSplit",
  "type": "object"
}

且该类型的序列化仅包括组合

timestamp
:

>>> t.model_dump_json()
'{"timestamp":"2024-09-24T11:00:00"}'
© www.soinside.com 2019 - 2024. All rights reserved.