Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples for functional_serializers in API docs #8546

Merged
merged 5 commits into from Jan 20, 2024
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 119 additions & 0 deletions pydantic/functional_serializers.py
Expand Up @@ -18,6 +18,28 @@
class PlainSerializer:
"""Plain serializers use a function to modify the output of serialization.

This is particularly helpful when you want to customize the serialization for annotated types.
Consider an input of `list`, which will be serialized into a space-delimited string.

```python
from typing import List

from typing_extensions import Annotated

from pydantic import BaseModel, PlainSerializer

CustomStr = Annotated[
List, PlainSerializer(lambda x: ' '.join(x), return_type=str)
]

class StudentModel(BaseModel):
courses: CustomStr

student = StudentModel(courses=['Math', 'Chemistry', 'English'])
print(student.model_dump())
#> {'courses': 'Math Chemistry English'}
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
```

Attributes:
func: The serializer function.
return_type: The return type for the function. If omitted it will be inferred from the type annotation.
Expand Down Expand Up @@ -61,6 +83,59 @@ class WrapSerializer:
"""Wrap serializers receive the raw inputs along with a handler function that applies the standard serialization
logic, and can modify the resulting value before returning it as the final output of serialization.

A scenario in which a model's subclass containing datetime fields is transformed to UTC without modifying subclass.
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved

```python
from datetime import datetime, timezone
from typing import Any, Dict

from typing_extensions import Annotated

from pydantic import BaseModel, WrapSerializer

class EventDatetime(BaseModel):
start: datetime
end: datetime

def convert_to_utc(value: Any, handler, info) -> Dict[str, datetime]:
# Note that `helper` can actually help serialize the `value` for further custom serialization in case it's a subclass.
partial_result = handler(value, info)
if info.mode == 'json':
return {
k: datetime.fromisoformat(v).astimezone(timezone.utc)
for k, v in partial_result.items()
}
return {k: v.astimezone(timezone.utc) for k, v in partial_result.items()}

UTCEventDatetime = Annotated[EventDatetime, WrapSerializer(convert_to_utc)]

class EventModel(BaseModel):
event_datetime: UTCEventDatetime

dt = EventDatetime(
start='2024-01-01T07:00:00-08:00', end='2024-01-03T20:00:00+06:00'
)
event = EventModel(event_datetime=dt)
print(event.model_dump())
'''
{
'event_datetime': {
'start': datetime.datetime(
2024, 1, 1, 15, 0, tzinfo=datetime.timezone.utc
),
'end': datetime.datetime(
2024, 1, 3, 14, 0, tzinfo=datetime.timezone.utc
),
}
}
'''

print(event.model_dump_json())
'''
{"event_datetime":{"start":"2024-01-01T15:00:00Z","end":"2024-01-03T14:00:00Z"}}
'''
```

Attributes:
func: The serializer function to be wrapped.
return_type: The return type for the function. If omitted it will be inferred from the type annotation.
Expand Down Expand Up @@ -151,6 +226,26 @@ def field_serializer(
) -> Callable[[Any], Any]:
"""Decorator that enables custom field serialization.

In the below example, a field of type `set` is used to mitigate duplication. A `field_serializer` is used to serialize the data as a sorted list.

```python
from typing import Set

from pydantic import BaseModel, field_serializer

class StudentModel(BaseModel):
name: str = 'Jane'
courses: Set[str]

@field_serializer('courses', when_used='json')
def serialize_courses_in_order(courses: Set[str]):
return sorted(courses)

student = StudentModel(courses={'Math', 'Chemistry', 'English'})
print(student.model_dump_json())
#> {"name":"Jane","courses":["Chemistry","English","Math"]}
```

See [Custom serializers](../concepts/serialization.md#custom-serializers) for more information.

Four signatures are supported:
Expand Down Expand Up @@ -217,6 +312,30 @@ def model_serializer(
) -> Callable[[Any], Any]:
"""Decorator that enables custom model serialization.

This is useful when a model need to be serialized in a customized manner, allowing for flexibility beyond just specific fields.

An example would be to serialize temperature to the same temperature scale, such as degrees Celsius.

```python
from typing import Literal

from pydantic import BaseModel, model_serializer

class TemperatureModel(BaseModel):
unit: Literal['C', 'F']
value: int

@model_serializer()
def serialize_model(self):
if self.unit == 'F':
return {'unit': 'C', 'value': int((self.value - 32) / 1.8)}
return {'unit': self.unit, 'value': self.value}

temperature = TemperatureModel(unit='F', value=212)
print(temperature.model_dump())
#> {'unit': 'C', 'value': 100}
```
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved

See [Custom serializers](../concepts/serialization.md#custom-serializers) for more information.

Args:
Expand Down