Validate the Date Format
Add a custom validator that checks the date field is in YYYY-MM-DD format
Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.
Why validation matters
Your Expense model already validates types and values. Pydantic rejects negative amounts, empty descriptions, and invalid categories automatically. But the date field is defined as Optional[str] = None — Pydantic checks that the value is a string (or None), but it does not check the format. A client can send "banana" as a date and your API will accept it.
In a production API, you would validate every input that has a specific format or business rule. This course focuses on building endpoints, so we will add one custom validator for the date field to show you the pattern. The same technique applies to any field that needs format checking.
How field validators work
Built-in constraints like Field(gt=0) handle simple rules. But format validation — checking that a string follows a specific pattern — requires custom logic. Pydantic's @field_validator decorator lets you write that logic as a method on your model. It receives the field value, validates it, and returns it. If the value is invalid, the method raises a ValueError, and Pydantic converts it into a validation error with a clear message.
@field_validator("date")
@classmethod
def validate_date_format(cls, v):
# v is the value of the date field
# return v if valid, raise ValueError if notA regular method uses self as its first argument — it works on a finished object. But when Pydantic validates input data, the object does not exist yet. The @classmethod decorator makes the method receive the class (cls) instead of an instance (self), so it can run before the object is created. Pydantic requires this for field validators.
Parsing dates with strptime
Python's datetime.strptime() parses a string into a date object using a format pattern. If the string does not match the pattern, it raises a ValueError. You can use this to validate the format without keeping the parsed result — just check that the parsing succeeds.
The format "%Y-%m-%d" matches strings like "2024-01-15" (four-digit year, two-digit month, two-digit day).
Instructions
Add a custom date validator to the Expense model.
- Add
from datetime import datetimeto the imports at the top of the file. - Add
field_validatorto the existing pydantic import, so it readsfrom pydantic import BaseModel, Field, field_validator. - Inside the
Expenseclass, after thedatefield, add the@field_validator("date")decorator followed by@classmethodon the next line. - Define a method named
validate_date_formatthat takes two parameters —clsandv— then inside it, writeif v is not None:. - Inside the
ifblock, add atry/except ValueErrorblock. In thetry, calldatetime.strptime(v, "%Y-%m-%d")— this attempts to parsevas a date using that exact format. If the string does not match, Python raises aValueErrorautomatically. Catch it and raiseValueError("Date must be in YYYY-MM-DD format"). - After the
ifblock, returnv.
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Literal, Optional
# Step 1: Add from datetime import datetime
# Step 2: Add field_validator to the pydantic import
app = FastAPI()
class Expense(BaseModel):
description: str = Field(min_length=1)
amount: float = Field(gt=0)
category: Literal["food", "transport", "entertainment", "utilities", "other"]
date: Optional[str] = None
# Step 3: Add @field_validator("date") and @classmethod
# Step 4: Define validate_date_format(cls, v), then: if v is not None:
# Step 5: try datetime.strptime(v, "%Y-%m-%d")
# except ValueError: raise ValueError("Date must be in YYYY-MM-DD format")
# Step 6: Return v
Interactive Code Editor
Sign in to write and run code, track your progress, and unlock all chapters.
Sign In to Start Coding