I'm using pydantic dataclasses to create a non-mutable dataclass that contains a name
, a label
and an id
. I want to be able to specify the label when creating a MyClass
object or give a default label that depends on the value of name
.
The current implementation only works when label
is part of the input arguments is the following:
@dataclass(frozen=True)
class MyClass
name: str = Field(default="class_name")
label: str = Field(default=None)
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
_validate_uuid = field_validator("id")(uuid_error)
@field_validator("label")
def set_default_label(cls, v, values):
if v is None:
v = foo(values["name"])
return v
...
Using this code, we can trigger two different behaviours:
MyClass(name="object_name", label=None)
returns the right object:
MyClass(name="object_name", label=foo("object_name"),...)
but if we rely on the default value for label
, e.g. using this:
MyClass(name="object_name")
then the validator is not called and label stays None
:
MyClass(name="object_name", label=None)
Without additional configuration default values are not validated in Pydantic. So you have to pass the corresponding config option to the definition of the data class. Check out the following example:
from pydantic import Field, field_validator, ConfigDict
import uuid
from pydantic.dataclasses import dataclass
def foo(name):
return name + "_label"
@dataclass(config=ConfigDict(validate_default=True))
class MyClass:
name: str = Field(default="class_name")
label: str = Field(default=None)
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
@field_validator("label", mode="before")
@classmethod
def set_default_label(cls, v, info):
if v is None:
v = foo(info.data["name"])
return v
print(MyClass())
Which prints:
MyClass(name='class_name', label='class_name_label', id='fda6cbe7-a756-4e93-9924-0cd024f59251')
In addition I think it is good to be explicit about the validation mode, and declaring the validation as a class method.
I hope this helps!