How to change date format in pydantic for validation and serialization?
For validation I am using @validator. Is there an solution for both cases?
How to change date format in pydantic for validation and serialization?
For validation I am using @validator. Is there an solution for both cases?
You can implement a custom json serializer by using pydantic's custom json encoders. Then, together with pydantic's custom validator, you can have both functionalities.
from datetime import datetime, timezone
from pydantic import BaseModel, validator
def convert_datetime_to_iso_8601_with_z_suffix(dt: datetime) -> str:
return dt.strftime('%Y-%m-%dT%H:%M:%SZ')
def transform_to_utc_datetime(dt: datetime) -> datetime:
return dt.astimezone(tz=timezone.utc)
class DateTimeSpecial(BaseModel):
datetime_in_utc_with_z_suffix: datetime
# custom input conversion for that field
_normalize_datetimes = validator(
"datetime_in_utc_with_z_suffix",
allow_reuse=True)(transform_to_utc_datetime)
class Config:
json_encoders = {
# custom output conversion for datetime
datetime: convert_datetime_to_iso_8601_with_z_suffix
}
if __name__ == "__main__":
special_datetime = DateTimeSpecial(datetime_in_utc_with_z_suffix="2042-3-15T12:45+01:00") # note the different timezone
# input conversion
print(special_datetime.datetime_in_utc_with_z_suffix) # 2042-03-15 11:45:00+00:00
# output conversion
print(special_datetime.json()) # {"datetime_in_utc_with_z_suffix": "2042-03-15T11:45:00Z"}
This variant also works in fastapi's serializer where I am actually using it in that way.
I think that pre validator can help here.
from datetime import datetime, date
from pydantic import BaseModel, validator
class OddDate(BaseModel):
birthdate: date
@validator("birthdate", pre=True)
def parse_birthdate(cls, value):
return datetime.strptime(
value,
"%d/%m/%Y"
).date()
if __name__ == "__main__":
odd_date = OddDate(birthdate="12/04/1992")
print(odd_date.json()) #{"birthdate": "1992-04-12"}
In case you don't necessarily want to apply this behavior to all datetimes, you can create a custom type extending datetime. For example, to make a custom type that always ensures we have a datetime with tzinfo set to UTC:
from datetime import datetime, timezone
from pydantic.datetime_parse import parse_datetime
class utc_datetime(datetime):
@classmethod
def __get_validators__(cls):
yield parse_datetime # default pydantic behavior
yield cls.ensure_tzinfo
@classmethod
def ensure_tzinfo(cls, v):
# if TZ isn't provided, we assume UTC, but you can do w/e you need
if v.tzinfo is None:
return v.replace(tzinfo=timezone.utc)
# else we convert to utc
return v.astimezone(timezone.utc)
@staticmethod
def to_str(dt:datetime) -> str:
return dt.isoformat() # replace with w/e format you want
Then your pydantic models would look like:
from pydantic import BaseModel
class SomeObject(BaseModel):
some_datetime_in_utc: utc_datetime
class Config:
json_encoders = {
utc_datetime: utc_datetime.to_str
}
Going this route helps with reusability and separation of concerns :)