from typing import Optional
@dataclass
class Event:
id: str
created_at: datetime
updated_at: Optional[datetime]
#updated_at: datetime = field(default_factory=datetime.now) CASE 1
#updated_at: Optional[datetime] = None CASE 2
@dataclass
class NamedEvent(Event):
name: str
When creating an event instance I will generally not have an updated_at
field. I can either pass the current time
as default value or add a value to it when making the insertion in the database and fetch it in subsequent uses of the object. Which is the better way?
As per my understanding, I cannot create a NamedEvent
instance without passing the updated_at field in case1 and case2 since I do not have a default value in name field.
The underlying problem that you have seems to be the same one that is described here. The short version of that post is that in a function signature (including the dataclass-generated __init__
method), obligatory arguments (like NamedEvent's name
) can not follow after arguments with default values (which are necessary to define the behavior of Event's updated_at
) - a child's fields will always follow after those of its parent.
So either you have no default values in your parent class (which doesn't work in this case) or all your child's fields need default values (which is annoying, and sometimes simply not feasible).
The post I linked above discusses some patterns that you can apply to solve your problem, but as a nicer alternative you can also use the third part party package pydantic
which already solved this problem for you. A sample implementation could look like this:
import pydantic
from datetime import datetime
class Event(pydantic.BaseModel):
id: str
created_at: datetime = None
updated_at: datetime = None
@pydantic.validator('created_at', pre=True, always=True)
def default_created(cls, v):
return v or datetime.now()
@pydantic.validator('updated_at', pre=True, always=True)
def default_modified(cls, v, values):
return v or values['created_at']
class NamedEvent(Event):
name: str
The default-value specification through validators is a bit cumbersome, but overall it's a very useful package that fixes lots of the shortcomings that you run into when using dataclasses, plus some more.
Using the class definition, an instance of NamedEvent
can be created like this:
>>> NamedEvent(id='1', name='foo')
NamedEvent(id='1', created_at=datetime.datetime(2020, 5, 2, 18, 50, 12, 902732), updated_at=datetime.datetime(2020, 5, 2, 18, 50, 12, 902732), name='foo')