Search code examples
pythondatabasepostgresqlfastapipydantic

Pydantic created at and updated at fields


I'm new to using Pydantic and I'm using it to set up the models for FastAPI to integrate with my postgres database. I want to make a model that has an updated_at and created_at field which store the last datetime the model was updated and the datetime the model was created. I figured created_at could be something like this:

created_at: datetime = datetime.now()

How would I do an updated_at so it updates the datetime automatically every time the model is updated?


Solution

  • You can use a validator which will update the field updated_at each time when some other data in the model will change. The root_validator and the validate_assignment config attribute are what you are looking for.

    This is the sample code:

    from datetime import datetime
    from time import sleep
    
    from pydantic import BaseModel,root_validator
    
    
    class Foo(BaseModel):
        data: str = "Some data"
        created_at: datetime = datetime.now()
        updated_at: datetime = datetime.now()
    
        class Config:
            validate_assignment = True
    
        @root_validator
        def number_validator(cls, values):
            values["updated_at"] = datetime.now()
            return values
    
    
    if __name__ == '__main__':
        bar = Foo()
        print(bar.dict())
        sleep(5)
        bar.data = "New data"
        print(bar.dict())
    

    and the output:

    {
     'data': 'Some data',
     'created_at': datetime.datetime(2022, 7, 31, 10, 41, 13, 176243),
     'updated_at': datetime.datetime(2022, 7, 31, 10, 41, 13, 179253)
    }
    
    {
     'data': 'New data',
     'created_at': datetime.datetime(2022, 7, 31, 10, 41, 13, 176243),
     'updated_at': datetime.datetime(2022, 7, 31, 10, 41, 18, 184983)
    }
    

    You can see that there is a difference in microseconds after object creation. If this is a problem you can set:
    updated_at: Optional[datetime] = None
    and modify the validator like this:

        @root_validator
        def number_validator(cls, values):
            if values["updated_at"]:
                values["updated_at"] = datetime.now()
            else:
                values["updated_at"] = values["created_at"]
            return values
    

    and the new output:

    {
     'data': 'Some data',
     'created_at': datetime.datetime(2022, 7, 31, 10, 54, 33, 715379), 
     'updated_at': datetime.datetime(2022, 7, 31, 10, 54, 33, 715379)
    }
    
    {
     'data': 'New data',
     'created_at': datetime.datetime(2022, 7, 31, 10, 54, 33, 715379),
     'updated_at': datetime.datetime(2022, 7, 31, 10, 54, 38, 728778)
    }