Reputation: 825
I am trying to use Pydantic v2 to generate JSON schemas for all my domain classes and to marshal my domain objects to and from JSON.
Suppose I have a class
class Component:
def __init__(self, component_id: int):
self.component_id = component_id
@property
def unique_id(self) -> str:
return f"{type(self).__name__}-{self.component_id}"
@classmethod
def parse(unique_id: str):
return Component(int(unique_id.split("-", 1)[1]))
and a BaseModel
class Machine(BaseModel):
part_1: Component
part_2: Component
part_3: Component
The basic behavior I want is to be able to perform the following operations:
# create a machine
my_machine = Machine(
part_1=Component(1),
part_2=Component(2),
part_3=Component(3),
)
# serialize to json
serialized_machine = my_machine.model_dump_json()
# deserialize to get my machine again
copy_of_my_machine = Machine.model_validate_json(serialized_machine)
# and, lastly, get the json schema for machines
schema_json = Machine.json.dumps(Machine.model_json_schema(), indent=2)
Importantly, though, when I serialize the components within the machine, I want to serialize them as strings using their unique id method.
If I add to Component
this schema-customization method:
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.str_schema()
then Machine.model_json_schema()
now returns the desired schema (with all the properties being typed as strings). But, having done this, I lose the ability to even construct my machine the way I could before:
Machine(
part_1=Component(1),
part_2=Component(2),
part_3=Component(3),
)
## => raises:
##
## pydantic_core._pydantic_core.ValidationError: 3 validation errors for Machine
## part_1
## Input should be a valid string [type=string_type, input_value=<__main__.Component object at 0x103315f10>, input_type=Component]
## For further information visit https://errors.pydantic.dev/2.1/v/string_type
## part_2
## Input should be a valid string [type=string_type, input_value=<__main__.Component object at 0x103315ee0>, input_type=Component]
## For further information visit https://errors.pydantic.dev/2.1/v/string_type
## part_3
## Input should be a valid string [type=string_type, input_value=<__main__.Component object at 0x103315130>, input_type=Component]
## For further information visit https://errors.pydantic.dev/2.1/v/string_type
In summary, how do I get what I want?
Upvotes: 1
Views: 1112
Reputation: 825
I figured out, based on another page in a different part of the docs, that I can, inside of Component
,
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
assert issubclass(source, Component)
return core_schema.json_or_python_schema(
json_schema=core_schema.str_schema(),
python_schema=core_schema.is_instance_schema(Component),
serialization=core_schema.plain_serializer_function_ser_schema(lambda c: c.unique_id)
)
This defines python validation logic separately from json validation logic.
Upvotes: 2