If you want the model to still hold an actual date object in that field, you should definitely leave the annotation as date_of_birth: date rather than simply switching to str.
If you want to allow strings with a particular format to be passed as a special case, you should define a pre=True validator that will check for strings and attempt to parse those accordingly. This is similar to @suraj's answer, but has the advantage of type safety.
For even more clarity, you could set your desired date format as a class variable on the module.
For the API, you could leverage the fact that the JSON Schema specs allow the format keyword for string types with custom format specifications. You could extend the automatically generated schema for the model to display for example that same date format for every date type field in the model instead of just showing the standard format called "date". Note that there is no special type for dates in JSON, so the Swagger UI would always have to show that field as string regardless.
Lastly, to make it crystal clear for users of the API what that format represents, you could add an example data object to the schema at the end.
Here is the full code I suggest:
from __future__ import annotations
from datetime import date, datetime
from typing import Any, ClassVar
from pydantic import BaseModel, validator
class Profile(BaseModel):
date_format: ClassVar[str] = "%d-%b-%Y"
example_data: ClassVar[dict[str, Any]] = {
"name": "John Doe",
"date_of_birth": "15-Jul-1996",
"gender": "m",
}
name: str
date_of_birth: date
gender: str
@validator("date_of_birth", pre=True)
def string_to_date(cls, v: object) -> object:
if isinstance(v, str):
return datetime.strptime(v, cls.date_format).date()
return v
class Config:
@staticmethod
def schema_extra(schema: dict[str, Any], model: type[Profile]) -> None:
for name, field in model.__fields__.items():
if field.type_ is date:
schema["properties"][name]["format"] = model.date_format
schema["examples"] = [model.example_data]
Demo:
profile_example = Profile.parse_obj(Profile.example_data)
print(profile_example.json(indent=4))
print(Profile.schema_json(indent=4))
Output:
{
"name": "John Doe",
"date_of_birth": "1996-07-15",
"gender": "m"
}
{
"title": "Profile",
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"date_of_birth": {
"title": "Date Of Birth",
"type": "string",
"format": "%d-%b-%Y"
},
"gender": {
"title": "Gender",
"type": "string"
}
},
"required": [
"name",
"date_of_birth",
"gender"
],
"examples": [
{
"name": "John Doe",
"date_of_birth": "15-Jul-1996",
"gender": "m"
}
]
}
Even if you don't want to go the extra mile with the schema customization, I would still argue for using the proper field type and pre-validating strings instead.
Here is the minimal version:
from datetime import date, datetime
from pydantic import BaseModel, validator
class Profile(BaseModel):
name: str
date_of_birth: date
gender: str
@validator("date_of_birth", pre=True)
def string_to_date(cls, v: object) -> object:
if isinstance(v, str):
return datetime.strptime(v, "%d-%b-%Y").date()
return v
The disadvantage is that the format default shown in the JSON Schema is date, which has a different meaning and is equivalent to %d-%m-%Y, which obviously does not fit your actual requirements. But parsing would still work of course with that validator.
python - FastAPI: datetime with timezone in request doesn't work - Stack Overflow
FastAPI Query Parameters with datetime.date not updating default value?
Using default for datetime query parameter without timezone leads to OpenAPI SpecValidationException
How to maintain the datetime format same as input
Videos
If you want the model to still hold an actual date object in that field, you should definitely leave the annotation as date_of_birth: date rather than simply switching to str.
If you want to allow strings with a particular format to be passed as a special case, you should define a pre=True validator that will check for strings and attempt to parse those accordingly. This is similar to @suraj's answer, but has the advantage of type safety.
For even more clarity, you could set your desired date format as a class variable on the module.
For the API, you could leverage the fact that the JSON Schema specs allow the format keyword for string types with custom format specifications. You could extend the automatically generated schema for the model to display for example that same date format for every date type field in the model instead of just showing the standard format called "date". Note that there is no special type for dates in JSON, so the Swagger UI would always have to show that field as string regardless.
Lastly, to make it crystal clear for users of the API what that format represents, you could add an example data object to the schema at the end.
Here is the full code I suggest:
from __future__ import annotations
from datetime import date, datetime
from typing import Any, ClassVar
from pydantic import BaseModel, validator
class Profile(BaseModel):
date_format: ClassVar[str] = "%d-%b-%Y"
example_data: ClassVar[dict[str, Any]] = {
"name": "John Doe",
"date_of_birth": "15-Jul-1996",
"gender": "m",
}
name: str
date_of_birth: date
gender: str
@validator("date_of_birth", pre=True)
def string_to_date(cls, v: object) -> object:
if isinstance(v, str):
return datetime.strptime(v, cls.date_format).date()
return v
class Config:
@staticmethod
def schema_extra(schema: dict[str, Any], model: type[Profile]) -> None:
for name, field in model.__fields__.items():
if field.type_ is date:
schema["properties"][name]["format"] = model.date_format
schema["examples"] = [model.example_data]
Demo:
profile_example = Profile.parse_obj(Profile.example_data)
print(profile_example.json(indent=4))
print(Profile.schema_json(indent=4))
Output:
{
"name": "John Doe",
"date_of_birth": "1996-07-15",
"gender": "m"
}
{
"title": "Profile",
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"date_of_birth": {
"title": "Date Of Birth",
"type": "string",
"format": "%d-%b-%Y"
},
"gender": {
"title": "Gender",
"type": "string"
}
},
"required": [
"name",
"date_of_birth",
"gender"
],
"examples": [
{
"name": "John Doe",
"date_of_birth": "15-Jul-1996",
"gender": "m"
}
]
}
Even if you don't want to go the extra mile with the schema customization, I would still argue for using the proper field type and pre-validating strings instead.
Here is the minimal version:
from datetime import date, datetime
from pydantic import BaseModel, validator
class Profile(BaseModel):
name: str
date_of_birth: date
gender: str
@validator("date_of_birth", pre=True)
def string_to_date(cls, v: object) -> object:
if isinstance(v, str):
return datetime.strptime(v, "%d-%b-%Y").date()
return v
The disadvantage is that the format default shown in the JSON Schema is date, which has a different meaning and is equivalent to %d-%m-%Y, which obviously does not fit your actual requirements. But parsing would still work of course with that validator.
Try this. You can use @validator
from pydantic import BaseModel, validator
from datetime import datetime
class Profile(BaseModel):
name: str
DOB: str # change this to str
gender: str
@validator('DOB')
def parse_dob(cls, v):
return datetime.strptime(v, '%d-%b-%Y').date() # convert string to date