about things
1# serialization
2
3## model_dump
4
5convert a model to a dict. control what's included:
6
7```python
8class Track(BaseModel):
9 id: int
10 title: str
11 artist_did: str
12 internal_score: float
13
14track.model_dump() # all fields
15track.model_dump(exclude={"internal_score"}) # omit internal fields
16track.model_dump(include={"id", "title"}) # only these
17track.model_dump(mode="json") # json-compatible types (datetimes → strings)
18```
19
20`mode="json"` is important — without it, you get python objects (datetime, Decimal). with it, everything is JSON-serializable.
21
22## model_dump_json / model_validate_json
23
24skip the dict intermediary for performance:
25
26```python
27# serialize directly to JSON string
28json_str = track.model_dump_json()
29
30# deserialize directly from JSON string
31track = Track.model_validate_json(json_str)
32```
33
34faster than `json.dumps(track.model_dump())` because pydantic uses rust-based serialization internally.
35
36useful for Redis caching:
37
38```python
39await redis.set(cache_key, response.model_dump_json(), ex=300)
40
41cached = await redis.get(cache_key)
42if cached:
43 return AlbumResponse.model_validate_json(cached)
44```
45
46## computed fields
47
48fields derived from other fields, included in serialization but not accepted as input:
49
50```python
51from pydantic import BaseModel, computed_field
52
53class Album(BaseModel):
54 tracks: list[Track]
55
56 @computed_field
57 @property
58 def track_count(self) -> int:
59 return len(self.tracks)
60
61 @computed_field
62 @property
63 def total_duration(self) -> float:
64 return sum(t.duration for t in self.tracks)
65```
66
67shows up in `model_dump()` and JSON output. not stored, always recomputed.
68
69## TypeAdapter for ad-hoc validation
70
71validate data without defining a full model:
72
73```python
74from pydantic import TypeAdapter
75
76int_adapter = TypeAdapter(int)
77int_adapter.validate_python("42") # → 42
78int_adapter.validate_python("abc") # → ValidationError
79
80# useful for parsing env var fragments
81StatusCode = Annotated[int, Field(ge=100, le=599)]
82code_adapter = TypeAdapter(StatusCode)
83code_adapter.validate_python(429) # → 429
84code_adapter.validate_python(999) # → ValidationError
85```
86
87also handles complex types: `TypeAdapter(list[int])`, `TypeAdapter(dict[str, float])`.
88
89## response models in FastAPI
90
91FastAPI uses pydantic models for automatic request validation and response serialization:
92
93```python
94class PlaylistResponse(BaseModel):
95 id: str
96 name: str
97 track_count: int
98 created_at: str
99
100@router.get("/playlists/{id}", response_model=PlaylistResponse)
101async def get_playlist(id: str) -> PlaylistResponse:
102 ...
103```
104
105the `response_model` strips any extra fields from the return value — useful when your internal object has more data than the API should expose.
106
107sources:
108- [pydantic docs: serialization](https://docs.pydantic.dev/latest/concepts/serialization/)
109- [pydantic docs: type adapter](https://docs.pydantic.dev/latest/concepts/type_adapter/)