How to validate date using pydantic?
Tags: pydatntic, python, date, validator
Published: 2023-07-05 06:14:39


Consider you have a data model that has start date and end date, we need to validate the start date and the end date. Start date must be greater than 1-JAN-2020 and end date must be greater than start date.

## How can we do that?

```python
from datetime import date
from pydantic import BaseModel, field_validator

class DateModel(BaseModel):
    st_date: date
    nd_date: date

    # Field validator for st_date
    @field_validator('st_date')
    def validate_start_date(cls, value: date) -> date:
        # Check if the value is less than January 1, 2020
        if value < date(2020, 1, 1):
            # Raise a ValueError with the error message
            raise ValueError("Start date cannot be before January 1, 2020.")
        # Return the value if it passes validation
        return value

    # Field validator for nd_date
    @field_validator('nd_date')
    def validate_end_date(cls, value: date, values) -> date:
        # Retrieve the value of st_date field from the values dictionary
        start_date = values.get('st_date')
        # Check if nd_date is less than or equal to st_date
        if value <= start_date:
            # Raise a ValueError with the error message
            raise ValueError("End date must be greater than the start date.")
        # Return the value if it passes validation
        return value
```

## Is there a better way to do it?

Yes, following code reduces the need for start date validation by validator function. However, end date needs a validator function.

```python
from datetime import date
from pydantic import BaseModel, Field, field_validator


class DateModel(BaseModel):
    # Define the st_date field with a validation rule using Field
    st_date: date = Field(..., gt=date(2020, 1, 1))

    # Define the nd_date field
    nd_date: date

    # Field validator for nd_date
    @field_validator("nd_date")
    def validate_end_date(cls, value: date, values, **kwargs) -> date:
        # Retrieve the value of nd_date field
        # Retrieve the value of st_date field from the values dictionary
        start_date = values.get("st_date")

        # Check if nd_date is less than or equal to st_date
        if value <= start_date:
            # Raise a ValueError with the error message
            raise ValueError("End date must be greater than the start date.")

        # Return the value if it passes validation
        return value
```

This code is an improvement over the previous code in terms of readability and conciseness. It makes use of Pydantic's built-in validation features, specifically the `Field` class and the `field_validator` decorator. Here's an explanation of how this code is better:

1. **Simplified Field Validation**: The code uses the `Field` class to define the `st_date` field with a validation rule. By specifying `gt=date(2020, 1, 1)`, it ensures that the `st_date` field must be greater than January 1, 2020. This eliminates the need for a separate validation method.

2. **Improved Error Handling**: In the previous code, the validation errors were raised as `ValueError` exceptions. In this code, the validation error is raised with the appropriate error message using the `raise` statement. This provides more meaningful error messages to the user.

## What is not possible?

In Pydantic, you cannot reference other fields directly within the Field declaration.

**The following code will not work!**

```python
from datetime import date
from pydantic import BaseModel, Field, field_validator


class DateModel(BaseModel):
    st_date: date = Field(..., gt=date(2020, 1, 1))
    nd_date: date = Field(..., gt=st_date)
```