-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
In 2.12, we should be able to have #10961 to land. It provides a way to conditionally exclude a field from the serialized output:
class Model(BaseModel):
a: int = Field(exclude_if=lambda v: v >= 0)
Model(a=1).model_dump()
#> {}
For now, the callable only takes one argument: the value to be serialized. This feature might get extended to have more info (e.g. the serialization context) provided. For instance, to exclude a field from the JSON output:
class Model(BaseModel):
a: int = Field(exclude_if=lambda v, info: info.mode == 'json')
This is great, but is a bit unfortunate: the serialization mode is known by pydantic-core
, and in theory we shouldn't need a Python call (which will hurt performance) to our lambda function to determine the exclusion of the field. We could introduce new exclude_*
flags, such as exclude_if_json
and exclude_if_python
(we already have exclude_none
fwiw), but this doesn't scale well (and we'll end up in a flag hell situation).
Introducing a new marker API
The API would roughly look like this:
class Marker(ABC): ...
@dataclass
class ModeIs(Marker):
mode: Literal['python', 'json']
class Model(BaseModel):
a: int = Field(exclude_if=ModeIs('json'))
Model(a=1).model_dump()
# {'a': 1}
Model(a=1).model_dump_json()
# {}
To avoid having to use a Python callable (which could hurt performance), similar flags could be introduced (that would be natively understood by pydantic-core
):
@dataclass
class ValueIs(Marker):
"""Marker that compares a value by identity."""
value: Any
my_sentinel = object()
class Model(BaseModel):
a: object = Field(exclude_if=ValueIs(my_sentinel))
Model(a=my_sentinel).model_dump()
#> {}
Other use cases
exclude_if
is one example of where this could be used, but could be used in other places:
class Model(BaseModel):
my_field: int = Field(alias='myField')
model_config = {
'validate_by_name': ModeIs('python'),
'validate_by_alias': ModeIs('json'),
}
Model.model_validate({'myField': 1}) # Validation error
Model.model_validate_json('{"my_field": 1}') # Validation error
Advanced operations
We could add operator support to these markers:
@dataclass
class Predicate(Marker):
predicate: Callable[[Any], bool]
class Model(BaseModel):
a: int = Field(exclude_if=ModeIs('json') & Predicate(lambda v: v >= 0))
Model(a=-1).model_dump_json()
# '{"a": -1}'
Model(a=1).model_dump_json()
# '{}'