6

My question is pretty simple and I'm surprised no one has asked it so far:

How can I validate dates in pydantic?

For example, I only want to accept dates in the range 1980.1.1-2000.1.1.

salius
  • 918
  • 1
  • 14
  • 30

2 Answers2

8

validator for datetime field is what you want. You can use it as following:

from pydantic import BaseModel, validator
from datetime import datetime

class DModel(BaseModel):
    dt: datetime

    @validator("dt")
    def ensure_date_range(cls, v):
        if not datetime(year=1980, month=1, day=1) <= v < datetime(year=2000, month=1, day=1):
            raise ValueError("Must be in range")
        return v

DModel.parse_obj({"dt": "1995-01-01T00:00"})
DModel.parse_obj({"dt": "2001-01-01T00:00"})  # validation error
alex_noname
  • 26,459
  • 5
  • 69
  • 86
3

One common use case, possibly hinted at by the OP's use of "dates" in the plural, is the validation of multiple dates in the same model. This is very lightly documented, and there are other problems that need to be dealt with you want to parse strings in other date formats.

This is not explicitly in the docs. Well, it's there, but the circumstances where it's required are not mentioned. If you need your validator to convert a non-ISO str format to date Pydantic will discover an invalid date format before your validator has run.

In these circumstances it's not sufficient to just apply multiple validators, or apply one validator to multiple fields, the pre=True argument also needs to be supplied, to pre-empt the default formatting from raising an exception, before your validator can convert it:

from datetime import datetime, date
from pydantic import BaseModel, validator


def validate_date(value: str) -> date:
    return datetime.strptime(value, "%d/%m/%Y").date()


class CommonModel(BaseModel):
    created: date
    modified: date
    confirmed: date
    closed: date

    _validate_dates = validator(
        'created', 'modified', 'confirmed', 'closed', pre=True, allow_reuse=True,
    )(validate_date)

data = {
    'created': '25/12/2000',
    'modified': '31/12/2000',
    'confirmed': '01/01/2001',
    'closed': '11/09/2001',
}

try:
    model = CommonModel(**data)
except Exception as e:
    print(e.json())

The docs on this are so light I had to discover this myself, and I haven't found anyone else pointing this out, so I thought a demonstration of this kind of code would be helpful here.

NeilG
  • 3,886
  • 2
  • 22
  • 30