I can't seem to figure out how to create a model where I have two timestamp fields and on init get both of them to get the same default dynamic value.
This will not work since it evaluates when the model is created.
import pydantic, time
from datetime import datetime
class DataModel(pydantic.BaseModel):
created: datetime = datetime.utcnow()
last_modified: datetime = created
print(DataModel().dict())
print(DataModel().dict())
# Output
{'created': datetime.datetime(2018, 9, 6, 8, 33, 7, 322270), 'last_modified': datetime.datetime(2018, 9, 6, 8, 33, 7, 322270)}
{'created': datetime.datetime(2018, 9, 6, 8, 33, 7, 322270), 'last_modified': datetime.datetime(2018, 9, 6, 8, 33, 7, 322270)}
Using the always=True method works (as documented) for the created field, but we will get None on the last_modified field. Most likely because the validator only kicks in when the object is created and last_modified gets its default during model load.
import pydantic, time
from datetime import datetime
class DataModel(pydantic.BaseModel):
created: datetime = None
last_modified: datetime = created
@pydantic.validator('created', pre=True, always=True)
def default_ts(cls, v):
return v or datetime.utcnow()
print(DataModel().dict())
print(DataModel().dict())
# Output
{'created': datetime.datetime(2018, 9, 6, 8, 37, 15, 788589), 'last_modified': None}
{'created': datetime.datetime(2018, 9, 6, 8, 37, 15, 880138), 'last_modified': None}
Using the same validator for both fields don't work either since it executes at different times.
import pydantic, time
from datetime import datetime
class DataModel(pydantic.BaseModel):
created: datetime = None
last_modified: datetime = None
@pydantic.validator('created', 'last_modified', pre=True, always=True)
def default_ts(cls, v):
return v or datetime.utcnow()
print(DataModel().dict())
print(DataModel().dict())
# Output
{'created': datetime.datetime(2018, 9, 6, 8, 40, 57, 985554), 'last_modified': datetime.datetime(2018, 9, 6, 8, 40, 57, 985569)}
{'created': datetime.datetime(2018, 9, 6, 8, 40, 57, 985690), 'last_modified': datetime.datetime(2018, 9, 6, 8, 40, 57, 985701)}
And lastly, I tried to have a seperate validator that reads the value from the created field, but here I get an error.
import pydantic, time
from datetime import datetime
class DataModel(pydantic.BaseModel):
created: datetime = None
last_modified: datetime = None
@pydantic.validator('created', pre=True, always=True)
def default_ts_created(cls, v):
return v or datetime.utcnow()
@pydantic.validator('last_modified', pre=True, always=True)
def default_ts_modified(cls, v):
return v or cls.created
print(DataModel().dict())
print(DataModel().dict())
# Output
[...]
File "/tmp/test.py", line 474, in default_ts_modified
return v or cls.created
AttributeError: type object 'DataModel' has no attribute 'created'
Is there any pattern that I can use to correctly get last_modified to be set to what ever created gets set to on object creation?
This is relatively clearly document:
validators are “class methods”, the first value they receive here will be the UserModel not an instance of UserModel
their signature can with be (cls, value) or (cls, value, *, values, config, field)
The point is that the validator is a class method, so it can't introspect the instance (the instance doesn't actually exist when the validator is being called), instead its signature can be changed to include the other values you want to look at:
import pydantic
from datetime import datetime
class DataModel(pydantic.BaseModel):
created: datetime = None
last_modified: datetime = None
@pydantic.validator('created', pre=True, always=True)
def default_ts_created(cls, v):
return v or datetime.utcnow()
@pydantic.validator('last_modified', pre=True, always=True)
def default_ts_modified(cls, v, *, values, **kwargs):
return v or values['created']
print(DataModel().dict())
Thank you for the example. I clearly didn't read the Validators section properly and was too focused on the Validate Always example with the timestamp.
no problem at all.
Most helpful comment
This is relatively clearly document:
The point is that the validator is a class method, so it can't introspect the instance (the instance doesn't actually exist when the validator is being called), instead its signature can be changed to include the other values you want to look at: